- 浏览: 3049194 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
使用Oracle/Sun JDK来运行Java程序的时候,大家或许有用过jstat工具来观察GC的统计数据,例如上一篇日志里的
以前写过一帖说明jstat工具显示的读数与jvmstat计数器之前的关系:用Java获取full GC的次数?(2)
可以知道,FGC列表示的是full GC次数,对应的jvmstat计数器是sun.gc.collector.1.invocations:
但是这个计数器在CMS GC里到底是指什么的次数呢?
在JDK 6的HotSpot VM中,Oracle/Sun有官方支持的GC只有CMS比较特殊(Garbage-First在JDK6里还没正式支持,不算在内):其它几种GC的每个周期都是完全stop-the-world的;而CMS的每个并发GC周期则有两个stop-the-world阶段——initial mark与final re-mark,其它阶段是与应用程序一起并发执行的。
如果CMS并发GC过程中出现了concurrent mode failure的话那么接下来就会做一次mark-sweep-compact的full GC,这个是完全stop-the-world的。
正是这个特征,使得CMS的每个并发GC周期总共会更新full GC计数器两次,initial mark与final re-mark各一次;如果出现concurrent mode failure,则接下来的full GC自己算一次。
如果说大家关心“GC次数”主要关心的其实是应用暂停次数的话,这么做倒也合理。但要注意的是在CMS里“暂停次数”并不等同于“GC次数”,CMS并发GC的一个周期叫“一次GC”但暂停了两次。
只不过有些人在从其它GC改为用CMS的时候会对“full GC次数”的显著增加感到不满,觉得是不是应该想办法调优来让“full GC次数”降下来。这里有几点:
1、CMS GC的设计初衷就是以降低GC latency为目标。如果一个应用产生垃圾的速度非常高的话,原本清除那些垃圾需要的时间并不会消失,CMS只是把它从一个大暂停分散到了多个阶段上,其中部分是暂停的,部分是并发的。所以暂停的次数本来就应该会增加,而每次停顿的时间则应该比较短——这是设计取舍的倾向性导致的。
2、为了更有效的实现并发,CMS GC进行的过程中必须保证堆里还有足够剩余空间来留给应用去分配对象,所以比起ParallelScavenge等别的实现CMS必须要提早一些触发并发GC的启动。如果从ParallelScavange迁移到CMS的时候不同事增大GC堆的大小,那么可以看到同样的应用在GC堆的占用率更低的时候就会触发GC了,所以GC次数增加了。
3、CMS GC中,“full GC次数”的计数器在每个并发GC周期里是增加2而不是增加1的。这也就是这篇日志最想说明的点:这个计数器说明了GC造成的应用暂停的次数,但并不代表CMS的并发GC周期的个数。由于full GC的计数器也会在完全stop-the-world的full GC中增加1,所以这个计数器也不准确代表并发GC周期个数的正好两倍。
4、一个CMS并发GC周期的触发原因只有一个;其中的两次暂停都是同一个原因引致的,例如说最初CMS old gen或者perm gen的使用率已经超过了某个阈值之类。
======================================================================
实现细节感兴趣的同学们,看代码~
CMSCollector里有_gc_counters用于记录jvmstat(或者说PerfData)需要的统计数据。这是个CollectorCounters类型的对象,里面有_invocations成员是用来记录GC次数的。
TraceCollectorStats用于辅助记录GC的次数。它在构造器里会将传入的CollectorCounters的invocation_counter()计数器自增1(自增1的具体逻辑在其基类的PerfTraceTimedEvent的构造器里)。
计数器增加1就是这里的_eventp->inc();
HotSpot里每个stop-the-world行为都用一个VM_Operation包装起来。与CMS相关的两个VM_Operation就是VM_CMS_Initial_Mark与VM_CMS_Final_Mark。
这两个VM_Operation的核心部分都调用了下面这个函数:
留意到其中的TraceCollectorStats tcs(counters());了么?这就让full GC的计数器增加了1。
也就是说CMS GC的两个暂停阶段各自会让full GC计数器增加1,于是整个CMS并发GC周期里该计数器就会增加2了。
======================================================================
追加:有人提醒我有这么一篇文章:
JDK6 Update 23 changes CMS Collection counters
所以顺便一提,我这篇blog用的是JDK 6 update 25对应的HotSpot 20来讲的。
如果大家关注JMX读出来CMS collections次数,请留意一下上面链接的文章。
$ jstat -gcutil `pgrep -u admin java` S0 S1 E O P YGC YGCT FGC FGCT GCT 37.21 0.00 99.81 12.87 76.82 1767 196.843 3085 2998.088 3194.931
以前写过一帖说明jstat工具显示的读数与jvmstat计数器之前的关系:用Java获取full GC的次数?(2)
可以知道,FGC列表示的是full GC次数,对应的jvmstat计数器是sun.gc.collector.1.invocations:
column { header "^FGC^" /* Full Collections */ data sun.gc.collector.1.invocations align right width 5 scale raw format "0" }
但是这个计数器在CMS GC里到底是指什么的次数呢?
在JDK 6的HotSpot VM中,Oracle/Sun有官方支持的GC只有CMS比较特殊(Garbage-First在JDK6里还没正式支持,不算在内):其它几种GC的每个周期都是完全stop-the-world的;而CMS的每个并发GC周期则有两个stop-the-world阶段——initial mark与final re-mark,其它阶段是与应用程序一起并发执行的。
Memory Management in the Java HotSpot™ Virtual Machine 写道
如果CMS并发GC过程中出现了concurrent mode failure的话那么接下来就会做一次mark-sweep-compact的full GC,这个是完全stop-the-world的。
正是这个特征,使得CMS的每个并发GC周期总共会更新full GC计数器两次,initial mark与final re-mark各一次;如果出现concurrent mode failure,则接下来的full GC自己算一次。
如果说大家关心“GC次数”主要关心的其实是应用暂停次数的话,这么做倒也合理。但要注意的是在CMS里“暂停次数”并不等同于“GC次数”,CMS并发GC的一个周期叫“一次GC”但暂停了两次。
只不过有些人在从其它GC改为用CMS的时候会对“full GC次数”的显著增加感到不满,觉得是不是应该想办法调优来让“full GC次数”降下来。这里有几点:
1、CMS GC的设计初衷就是以降低GC latency为目标。如果一个应用产生垃圾的速度非常高的话,原本清除那些垃圾需要的时间并不会消失,CMS只是把它从一个大暂停分散到了多个阶段上,其中部分是暂停的,部分是并发的。所以暂停的次数本来就应该会增加,而每次停顿的时间则应该比较短——这是设计取舍的倾向性导致的。
2、为了更有效的实现并发,CMS GC进行的过程中必须保证堆里还有足够剩余空间来留给应用去分配对象,所以比起ParallelScavenge等别的实现CMS必须要提早一些触发并发GC的启动。如果从ParallelScavange迁移到CMS的时候不同事增大GC堆的大小,那么可以看到同样的应用在GC堆的占用率更低的时候就会触发GC了,所以GC次数增加了。
3、CMS GC中,“full GC次数”的计数器在每个并发GC周期里是增加2而不是增加1的。这也就是这篇日志最想说明的点:这个计数器说明了GC造成的应用暂停的次数,但并不代表CMS的并发GC周期的个数。由于full GC的计数器也会在完全stop-the-world的full GC中增加1,所以这个计数器也不准确代表并发GC周期个数的正好两倍。
4、一个CMS并发GC周期的触发原因只有一个;其中的两次暂停都是同一个原因引致的,例如说最初CMS old gen或者perm gen的使用率已经超过了某个阈值之类。
======================================================================
实现细节感兴趣的同学们,看代码~
CMSCollector里有_gc_counters用于记录jvmstat(或者说PerfData)需要的统计数据。这是个CollectorCounters类型的对象,里面有_invocations成员是用来记录GC次数的。
CollectorCounters::CollectorCounters(const char* name, int ordinal) { if (UsePerfData) { EXCEPTION_MARK; ResourceMark rm; const char* cns = PerfDataManager::name_space("collector", ordinal); _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1); strcpy(_name_space, cns); // ... cname = PerfDataManager::counter_name(_name_space, "invocations"); _invocations = PerfDataManager::create_counter(SUN_GC, cname, PerfData::U_Events, CHECK); // ... } }
TraceCollectorStats用于辅助记录GC的次数。它在构造器里会将传入的CollectorCounters的invocation_counter()计数器自增1(自增1的具体逻辑在其基类的PerfTraceTimedEvent的构造器里)。
class TraceCollectorStats: public PerfTraceTimedEvent { protected: CollectorCounters* _c; public: inline TraceCollectorStats(CollectorCounters* c) : PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()), _c(c) { if (UsePerfData) { _c->last_entry_counter()->set_value(os::elapsed_counter()); } } inline ~TraceCollectorStats() { if (UsePerfData) _c->last_exit_counter()->set_value(os::elapsed_counter()); } };
class PerfTraceTimedEvent : public PerfTraceTime { protected: PerfLongCounter* _eventp; public: inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp): PerfTraceTime(timerp), _eventp(eventp) { if (!UsePerfData) return; _eventp->inc(); } inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp, int* recursion_counter): PerfTraceTime(timerp, recursion_counter), _eventp(eventp) { if (!UsePerfData) return; _eventp->inc(); } };
计数器增加1就是这里的_eventp->inc();
HotSpot里每个stop-the-world行为都用一个VM_Operation包装起来。与CMS相关的两个VM_Operation就是VM_CMS_Initial_Mark与VM_CMS_Final_Mark。
// The VM_CMS_Operation is slightly different from // a VM_GC_Operation -- and would not have subclassed easily // to VM_GC_Operation without several changes to VM_GC_Operation. // To minimize the changes, we have replicated some of the VM_GC_Operation // functionality here. We will consolidate that back by doing subclassing // as appropriate in Dolphin. // // VM_Operation // VM_CMS_Operation // - implements the common portion of work done in support // of CMS' stop-world phases (initial mark and remark). // // VM_CMS_Initial_Mark // VM_CMS_Final_Mark //
这两个VM_Operation的核心部分都调用了下面这个函数:
void CMSCollector::do_CMS_operation(CMS_op_type op) { gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps); TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t("GC", PrintGC, !PrintGCDetails, gclog_or_tty); TraceCollectorStats tcs(counters()); switch (op) { case CMS_op_checkpointRootsInitial: { SvcGCMarker sgcm(SvcGCMarker::OTHER); checkpointRootsInitial(true); // asynch if (PrintGC) { _cmsGen->printOccupancy("initial-mark"); } break; } case CMS_op_checkpointRootsFinal: { SvcGCMarker sgcm(SvcGCMarker::OTHER); checkpointRootsFinal(true, // asynch false, // !clear_all_soft_refs false); // !init_mark_was_synchronous if (PrintGC) { _cmsGen->printOccupancy("remark"); } break; } default: fatal("No such CMS_op"); } }
留意到其中的TraceCollectorStats tcs(counters());了么?这就让full GC的计数器增加了1。
也就是说CMS GC的两个暂停阶段各自会让full GC计数器增加1,于是整个CMS并发GC周期里该计数器就会增加2了。
======================================================================
追加:有人提醒我有这么一篇文章:
JDK6 Update 23 changes CMS Collection counters
所以顺便一提,我这篇blog用的是JDK 6 update 25对应的HotSpot 20来讲的。
如果大家关注JMX读出来CMS collections次数,请留意一下上面链接的文章。
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
Java 8与静态工具类
2014-03-19 08:43 16278以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10453先看看下面这个代码例子, interface IFoo { ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
基于LLVM实现VM的JIT的一些痛点
2014-01-07 17:25 0同事Philip Reames Sanjoy Das http ... -
tailcall notes
2013-12-27 07:42 0http://blogs.msdn.com/b/clrcode ... -
《自制编程语言》的一些笔记
2013-11-24 00:20 0http://kmaebashi.com/programmer ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22396(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局 (0): 拿在手上的是什么
2013-11-04 18:22 21495(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21874之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ...
相关推荐
此外,项目中配置的是 CMS,为什么没有进行 CMS GC,而直接进行了 Full GC 呢? 查找过程 1. 代码是否调用了 System.gc() 首先,我们怀疑是代码中是否调用了 System.gc(),从而导致 Full GC。使用反射去监控 ...
3. **GC计数器**:包括Minor GC次数和Major GC次数,分别对应年轻代和老年代的垃圾收集次数。 4. **GC时间**:Minor GC和Major GC所消耗的时间,可以用来评估垃圾收集对应用性能的影响。 5. **eden/s0/s1/tenured...
- 碎片监控与GC周期初始化调优:利用特定JVM参数监控内存碎片情况,合理调整LDS与分配率,避免过早或过晚的GC启动。 - 避免promotion failed和concurrent mode failure:调整SurvivorSpace大小或老年代大小,降低...
- **Major GC**/Full GC:老年代的垃圾收集,可能涉及整个堆。 - **CMS(Concurrent Mark Sweep)**:并发标记清除,降低停顿时间。 - **G1(Garbage-First)**:新一代的垃圾收集器,目标是减少停顿时间。 #### ...
- **Full GC**:年老代被写满、持久代被写满、显示调用`System.gc()`、上一次GC之后Heap的各域分配策略动态变化、RMI等的定时触发。 #### 五、JVM监控与故障处理工具 JVM提供了多种工具帮助开发者监控JVM的状态...
4. Young GC 和 Full GC 触发条件;CMS 回收器的标记过程,内存碎片问题。 5. JVM 问题分析和命令:GC 频率,jstat -gcutil pid 1000;Thread dump 信息找 CPU 高的线程信息;Heap dump,OOM 分析 jmap 命令。 Java...
例如,新生代的Minor GC通常在Eden区满时触发,而Full GC可能在系统内存不足或手动请求时发生。过度频繁的GC或者长时间的垃圾收集可能会对程序性能造成影响,这时需要进行JVM调优。 类加载过程是Java程序运行的重要...
2. **GC(Garbage Collection)机制**:JVM自动进行垃圾回收,包括新生代GC(Minor GC)、老年代GC(Major GC)和全堆GC(Full GC)。了解不同GC算法(如Serial、Parallel、CMS、G1等)的工作原理和适用场景,可以...
6. **JVM监控与诊断工具**:使用JVisualVM、JConsole、jmap、jstat等工具监控JVM状态,分析内存泄漏、CPU过高、GC性能等问题。 7. **编译优化**:JIT(Just-In-Time)编译器将热点代码编译为机器码,提高执行效率。...
- 垃圾收集:GC算法(Minor GC、Major GC、Full GC)、垃圾收集器(Serial、Parallel、CMS、G1)。 - 性能调优:JDK工具(jps、jstat、jmap、jhat、jconsole等)、JVM参数设置。 以上内容仅是Java面试中的冰山...
- 增大新生代大小,注意这可能会导致Full GC变得更频繁。 - **缩短YGC造成的暂停时间**: - 减小新生代大小,可能会导致更多对象晋升到老年代。 - 增加或升级CPU。 #### 经验分享 - **Parallel Old GC调优**: -...
1. **GC日志分析**:通过分析GC日志,可以了解GC的行为模式,如full GC频率、暂停时间等。 2. **CMS GC日志分析**:Concurrent Mark Sweep(CMS)是一种低暂停时间的垃圾回收器,其日志分析有助于优化CMS配置。 ###...
4. **Java服务与GC**:检查是否有Java服务正在进行频繁的Full Garbage Collection (FGC),`jstat -gcutil pid`命令可以查看GC的状态。正常的GC行为不应导致长时间的内存压力。 5. **Java进程对比**:对比不同服务器...
Full GC通常在以下情况下触发: 1. 老年代空间不足时,将新生代存活对象移动到老年代,老年代空间也不足。 2. 永久代(方法区)空间不足时。 3. 显示调用System.gc()时。 4. 老年代的连续空间小于新生代对象的总...
- 调优目标之一是减少垃圾收集(GC)的频率和Full GC的发生,因为它们可能导致系统资源占用过多和性能下降。 - Full GC通常由于旧生代空间不足、新生代设置过大或过小、Survivor设置不合理等问题触发。 6. **JVM...
- `-Xmn512m`: 设置年轻代的大小为512MB,这有助于控制年轻代与老年代的比例,减少Full GC的发生。 - `-XX:MaxPermSize=256m`: 设置持久代的最大大小,用于存储类元数据。 - `-XX:+PrintGCDetails -XX:+...
Java的垃圾回收机制(GC)是自动管理内存的关键,分为Minor GC、Major GC(也称为老年代GC)和Full GC,它们分别针对年轻代、老年代和整个堆进行回收。垃圾回收器有多种,如Serial、Parallel、Concurrent Mark Sweep...
- **内存回收策略**:调整新生代与老年代的晋升策略,如设置Eden与Survivor区的比例,设置触发Full GC的条件。 3. **JVM参数设置** - **-Xms与-Xmx**:设置堆内存的初始大小和最大大小。 - **-XX:NewRatio**:...
- 内存调优:根据应用需求调整堆大小,减少Full GC。 - JIT编译:利用JIT将热点代码编译为机器码,提高执行效率。 - 并发与多线程:理解锁的原理,使用并发容器,避免死锁和活锁。 6. **JVM调优工具** - ...