学习JVM原理-Heap并不是越大越好
Heap越大,意味着里面可以放的对象越多,一旦发生Full GC,停顿会很长 Heap如果特别大,一旦发生内存溢出,生成Dump的就会非常大,大到几乎无法分析 周志明说:“给JVM分配超大堆的前提是有把握把应用程序的Full GC频率控制得足够低,并且保证程序足够稳定,不会产生堆溢出“
Heap越大,意味着里面可以放的对象越多,一旦发生Full GC,停顿会很长 Heap如果特别大,一旦发生内存溢出,生成Dump的就会非常大,大到几乎无法分析 周志明说:“给JVM分配超大堆的前提是有把握把应用程序的Full GC频率控制得足够低,并且保证程序足够稳定,不会产生堆溢出“
摘自《深入理解Java虚拟机》周志明著 Direct Memory满了之后,系统不会自动回收这段内存; 而是要等Tenured Generation满触发GC时,Direct Memory才会被跟着回收。 所以这一块很容易发生内存溢出. 为了防止这种事发生,你要么不把Heap设的过多,该Full GC的时候就Full GC; 要么在JVM参数里不禁止System.gc(),因为NIO的实现里会自己调用System.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 …
摘自《深入理解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)
摘自《深入理解Java虚拟机》周志明著 1. 哪些内存区域需要GC ? Runtime Data Areas中,Program Counter Register, VM Stack, Native Stack随方法/线程而生,随方法/线程而灭,这里基本不需要考虑内存回收的问题。 GC的讨论主要针对Heap和Method Area。 2. GC需要关心的问题 a.如何给对象判死刑?何时执行? –可回收标准 b.对象是一个一个杀,还是成批的杀? — 回收算法 c.对象杀之前关在哪里? — 内存分配 这些问题会在本系列后面的博客中陈述
摘自《深入理解Java虚拟机》周志明著 对象能否被GC,是根据对象关联图来决定的 (图论里的“图”) JVM会维持一张图,这个图里有些“根”结点, 如果从根到某个对象之间没有路径,则这个对象可以被GC. 这些根结点称作GC Roots,这种判断算法称作GC Roots Tracing Method Areas中的常量池的常量能否被GC,也是基于相同的原则; 至于Method Areas中的类,则要满足实例已回收、ClassLoader已回收和Bean.class对象未被引用等三个条件 不过,对象被判定未被引用之后并不会立即判死刑,它只会被放到一个队列中,系统会执行队列中每个对象的finalize()方法,在finalize()方法中对象可以把自己关联给别的对象,这样它就能逃出生天。然而,覆盖finalize()方法本身是不良的实践,应尽量避免。 对象被判死刑后并不意味着会立即执行,这取决于GC的调度算法,有的情况会频繁一些,有的则少一些,这取决于你使用的GC策略和当前的内存情况。
摘自《深入理解Java虚拟机》周志明著 答案:批量处理 具体有两种做法: 1.Mark(判死刑),然后批量sweep (Mark-Sweep算法) 2.把所有对象都放到Heap中的一块区域中,这个区域可以理解为一个监狱;监狱塞满人时,就一次性找出尚不须回收的对象,把它们复制到Heap中的空闲区域中,然后投弹夷平监狱,清除出整个内存区域;新的关押区域即成新的监狱,它也终将被夷平 (整个算法叫做copying 算法) 第一种做法中,由于存活对象跟可回收对象混杂在一起,可回收对象们没有集中在一起,因此要一个一个清除,效率会比较低(相当于挨家挨户搜查杀人),而且还会造成内存碎片,不利于大对象的分配 第二种模式效率高,且没有内存碎片,但由于它总是用一块内存,空置一块内存,这会造成内存浪费。
摘自《深入理解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 …
虚拟机圈子: http://hllvm.group.iteye.com 莫枢的博客: http://rednaxelafx.iteye.com 周志明: http://icyfenx.iteye.com/blog/1119214 周志明的微博: http://t.sina.com.cn/icyfenix
摘自《深入理解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),这样的停顿时间就很长了。 …