Month: September 2011

学习JVM原理-8.一个GC调优的实例

这个例子算是《深入理解Java虚拟机》第5章中某个例子的简写版。 1. 症状:系统启动比较慢,可能跟GC不力有关 2. 诊断:通过 打印GC日志,看看有没有什么不正常   a.为了打印GC日志,系统启动时加上三个JVM参数:     -XX:+PrintGCTimeStamps     -XX:+PrintGCDetails     -verbose:gc   b.启动系统,得到GC日志,这时可以重点关注Full GC,因为Full GC的时间比Minor GC长的多: 16.400: [Full GC 16.400: [Tenured: 53K->8872K(87424K), 0.5714300 secs] 26375K->8872K(126720K), [Perm : 16384K->16384K( 16384K)], 0.5715650 secs] [Times: user=0.26 sys=0.01, real=0.58 secs] 20.159: [Full GC 20.159: [Tenured: 8872K->10445K(87424K), 0.3710940 secs] 18205K->10445K(126848K), [Perm : 20479K->20479K( 20480K)], 0.3711840 secs] [Times: user=0.27 sys=0.00, real=0.38 …

学习JVM原理-8.一个GC调优的实例 Read More »

学习JVM原理-Direct Memory只会在Full GC时回收

摘自《深入理解Java虚拟机》周志明著 Direct Memory满了之后,系统不会自动回收这段内存; 而是要等Tenured Generation满触发GC时,Direct Memory才会被跟着回收。 所以这一块很容易发生内存溢出. 为了防止这种事发生,你要么不把Heap设的过多,该Full GC的时候就Full GC; 要么在JVM参数里不禁止System.gc(),因为NIO的实现里会自己调用System.gc()

学习JVM原理-Heap并不是越大越好

Heap越大,意味着里面可以放的对象越多,一旦发生Full GC,停顿会很长 Heap如果特别大,一旦发生内存溢出,生成Dump的就会非常大,大到几乎无法分析 周志明说:“给JVM分配超大堆的前提是有把握把应用程序的Full GC频率控制得足够低,并且保证程序足够稳定,不会产生堆溢出“

学习JVM原理-7.JVM监控工具

摘自《深入理解Java虚拟机》周志明著 1.jdk自带的文字界面工具 a. jps, 查看所有JAVA进程列表: jps -lvm b. jstat, 统计信息监控,如内存、GC等,如jstat -gcutil pid c.查看虚拟机的各项参数: sudo jinfo -flags pid d.打印System.getProperties(): sudo jinfo -sysprops  pid e. jmap, 查看Memory映像: jmap -dump:forbmat=b, file=dump.bin pid (dump.bin这个文件可以用Eclipse Memory Analyzer)来分析 f. jstack,生成线程快照,查找线程停顿原因的利器:sudo jstack -l -F  pid 2.Oracle/Sun提供的可视化工具 a. JConsole,通过JMX进行监控 (path: jdk/bin/jconsole) b. VisualVM,什么都可以做,而且可以直接应用在生产环境中,因为它不需要被监控的系统打开任何监控接口(但要在同一台机上另建一个基于rmi的jstatd服务) (path: jdk/bin/jvisualvm)

关于System.gc()

javadoc说: Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.   注意,只是“建议”JVM干这样一种事,不是直接命令 …

关于System.gc() Read More »

Hotspot中的Permanent Generation

Permanent Generation跟Young Generation, Tenured Generation不是一个层次的概念,Young和Tenured是指Heap内部的划分,而Permanent Generation其实就是规范里的Method Area

学习JVM原理-6.HotSpot支持的垃圾回收器

摘自《深入理解Java虚拟机》周志明著 垃圾回收器是GC算法在虚拟机中的具体实现。 HotSpot针对Young Generation和Tenured Generation分别提供了一些回收器,你可以按照你的系统要求来选取合适的组合, 考量因素包括:服务端系统还是客户端系统,追求用户体验还是希望不浪费CPU空间等 HotSpot提供的回收器:   1. Young Generation: Serial,  ParNew, Parallel Scavenge   2. Old Generation: Serial Old, CMS, Parallel Old Serial和Serial Old在GC时会停止其他工作线程(术语: Stop the world),这样回收效率最高,但代价是用户可能要容忍长时间的停顿;这个组合是Client模式下的默认选项,因为Client模式下一次性需要回收的内存并不大,虽然GC时会有停顿,但停顿时间一般会在一百毫秒以内 ParNew是高版本的Serial,它照样会Stop the world,只不过在多CPU环境下它的表现更好 Parallel Scavenge + Parallel Old组合的目标是为了尽最大可能把CPU精力放在工作上,而不是放在GC上(术语:吞吐率高);它适于在后台运算而不太需要太多交互的任务 CMS(Concurrent Mark Sweep)模式下,GC线程与工作线程同时工作,虽然有时也需Stop the world,但这个停顿时间非常短,因此 CMS模式适用于用户交互的系统,如互联网。 但CMS也有它的缺点:   a.与工作线程并发工作,因此对CPU的并发性要求比较高,最好用在多核(>= 4)的系统中,否则可能得不偿失   b.由于GC的过程中工作线程会产生新的垃圾(floating garbage), 如果等内存塞满了才开始GC,这些新的垃圾就没地方放,所以必须在塞满前就开始GC; 这可能导致GC的频率比较高,更严重的是,如果floating garbage的大小超过了当前所能容纳的空间,JVM会启动一次Full GC(基于Serial Old),这样的停顿时间就很长了。 …

学习JVM原理-6.HotSpot支持的垃圾回收器 Read More »

学习JVM原理-5.Young Generation和Tenured Generation

摘自《深入理解Java虚拟机》周志明著 GC的算法不止一种,不同的对象适用不同的算法。 因此JVM对内存进行了分区,不同区域适用不同算法。 一般分作Young Generation和Tenured Generation两个区域   1. Young Generation一般用Copying算法,但空闲部分只设10%(因为研究表明GC时大部分对象都会被杀);存活对象转移时,如果空闲部分不够装,则把装不下的放到Tenured Generation中(参见Eden, Survivor等术语)   2. Tenured Generation用来装Young Generation中转移过来的对象(在Young Generatioin中经历了足够多次GC,或者Young Generation中已放不下),已及一些不方便放在Young Generation中的对象,如大对象等。 Tenured Generation一般用Mark-Sweep算法或其改良版      Young Generation可以再分为 两部分: Young generation memory consists of two parts, Eden space and survivor space. Shortlived objects will be available in Eden space. Every object starts its life from Eden space. When …

学习JVM原理-5.Young Generation和Tenured Generation Read More »

学习JVM原理-4.GC算法:批量处理还是一个一个处理

摘自《深入理解Java虚拟机》周志明著 答案:批量处理 具体有两种做法:   1.Mark(判死刑),然后批量sweep (Mark-Sweep算法)   2.把所有对象都放到Heap中的一块区域中,这个区域可以理解为一个监狱;监狱塞满人时,就一次性找出尚不须回收的对象,把它们复制到Heap中的空闲区域中,然后投弹夷平监狱,清除出整个内存区域;新的关押区域即成新的监狱,它也终将被夷平 (整个算法叫做copying 算法) 第一种做法中,由于存活对象跟可回收对象混杂在一起,可回收对象们没有集中在一起,因此要一个一个清除,效率会比较低(相当于挨家挨户搜查杀人),而且还会造成内存碎片,不利于大对象的分配 第二种模式效率高,且没有内存碎片,但由于它总是用一块内存,空置一块内存,这会造成内存浪费。

学习JVM原理-3. 什么对象可被GC? 何时GC?

摘自《深入理解Java虚拟机》周志明著 对象能否被GC,是根据对象关联图来决定的 (图论里的“图”) JVM会维持一张图,这个图里有些“根”结点, 如果从根到某个对象之间没有路径,则这个对象可以被GC. 这些根结点称作GC Roots,这种判断算法称作GC Roots Tracing Method Areas中的常量池的常量能否被GC,也是基于相同的原则; 至于Method Areas中的类,则要满足实例已回收、ClassLoader已回收和Bean.class对象未被引用等三个条件 不过,对象被判定未被引用之后并不会立即判死刑,它只会被放到一个队列中,系统会执行队列中每个对象的finalize()方法,在finalize()方法中对象可以把自己关联给别的对象,这样它就能逃出生天。然而,覆盖finalize()方法本身是不良的实践,应尽量避免。 对象被判死刑后并不意味着会立即执行,这取决于GC的调度算法,有的情况会频繁一些,有的则少一些,这取决于你使用的GC策略和当前的内存情况。