[url]http://www.iteye.com/topic/802638[/url]
仔细阅读完上面的文章,我有如下总结:
[size=large][b]jvm内存模型是什么[/b][/size]
不了解内存,而已去内存垃圾收集?
查资料发现主要是三大块:新生代,老生代,永久代。
很好理解,就是根据对象的变化频率划分的。
[size=large][b]什么是GC[/b][/size]
Garbage Collection,很好理解,就是把不要的对象移除出内存。
[size=large][b]为什么要GC[/b]
[/size]很简单,因为内存对于现在的计算机来说还算是稀缺资源,所以要整理一些垃圾数据。
[size=large][b]什么时候GC[/b]
[/size]不看实现,如果让我实现,无非如下几种情况:
1.空间满了。
2.时间空闲了。
3.外部告诉JVM强制垃圾收集,比如system.gc()。
[size=large][b]怎么样GC[/b]
[/size]好了,这个是本文的重点,而怎么样做垃圾收集关键点又是什么呢?很容易想到就是怎么判断哪些对象应该垃圾收集以及怎么做垃圾收集最快最省力。
[b]第一个关键点:怎么判断哪些垃圾收集?[/b]有人想到了一个简单实现,那就是引用计数法,如果引用一个对象就把这个对象的引用数+1,如果取消一个对象的引用就把这个对象的引用数-1。好了,如果引用数如果为0,那么我们说这个对象应该被垃圾收集。
当然,我想这个算法是很简洁易懂,但是并不完美。对于局部循环引用的的情况下,那两个对象会抱团存活(互相引用)。而这不是我们想要的。我们需要一并清理这些作假者。
基于如上问题,人们想到了另外一种实现,那就是根搜索算法,可以这么说,任何引用其他对象的引用都可以找到源头的一个对象,因此可以得出一个引用链,我们只要记录这个根引用的集合。所以,如果一个对象的任何引用追溯到根节点都不在根引用集合里面,那么即使这个对象即使被不是根引用的对象引用着也是可以被垃圾回收的。
[b]第二个关键点:怎么保证垃圾收集又快又省力?也就是高效[/b]
有人想到了如下算法,内存分成两半,一半用于工作内存,另外一半不用,这样,如果其中工作内存满了的时候就进行一次垃圾收集,收集的方式就是把这个满的一半中还存活的对象拷贝到另一半中就可以了,这个方法可以说是简单明了,速度很快,因为只是单纯的拷贝。
这个算法很高效但是不是很完美,首先很明显内存浪费了,因为有一半没有被利用。
于是人们想到了能否改变一下分配比例?比如不是1:1,而是8:2之类的。
据IBM调查发现新生代的对象是98%都是很快需要垃圾收集的。(证据需要考证)
因此人们把内存块划分为8(eden):1(survivor):1(survivor),这样平时用到eden区和一个survivor区。到内存满的时候拷贝到另外一个survivor里面,然后把其他的空间清理掉,然后进入下一个eden+survivor的工作模式。
上面两种算法都可以叫做mark-copy算法。
这似乎很好的解决了空间利用率的问题,但是我们看到有个前提就是对象98%需要垃圾收集。
对于那些需要垃圾收集的对象比例不高的情况下,这个复制算法就显得效率低下了。
因此人们想到了非全拷贝的算法。那就是mark-compact算法。
它的原理很简单,就是在垃圾收集的时候把还存活的对象往一端移动,尽量堆在一起,去掉内存碎片,然后把边界以外的内存空间清理掉就行了。
这个对于老生代或者持久代来说是很好的,因为,要拷贝的对象很少。所以代价最小,速度很高。
基于以上两种算法,现在商业jvm就把上述三种内存模式又细分为六种情况。
总体来说也就是三类:
串行收集(但CPU)
并行收集(多CPU)
并发收集(stop the world时间最小,实时性要求高)
[size=large][b]总结[/b][/size]
基于以上理解回过头来看看jvm的参数就很容易理解了。
[b]堆设置[/b]
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
[b]收集器设置[/b]
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
[b]垃圾回收统计信息[/b]
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
[i][u][b]并行收集器设置[/b][/u][/i]
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
[u][i][b]并发收集器设置[/b][/i][/u]
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
仔细阅读完上面的文章,我有如下总结:
[size=large][b]jvm内存模型是什么[/b][/size]
不了解内存,而已去内存垃圾收集?
查资料发现主要是三大块:新生代,老生代,永久代。
很好理解,就是根据对象的变化频率划分的。
[size=large][b]什么是GC[/b][/size]
Garbage Collection,很好理解,就是把不要的对象移除出内存。
[size=large][b]为什么要GC[/b]
[/size]很简单,因为内存对于现在的计算机来说还算是稀缺资源,所以要整理一些垃圾数据。
[size=large][b]什么时候GC[/b]
[/size]不看实现,如果让我实现,无非如下几种情况:
1.空间满了。
2.时间空闲了。
3.外部告诉JVM强制垃圾收集,比如system.gc()。
[size=large][b]怎么样GC[/b]
[/size]好了,这个是本文的重点,而怎么样做垃圾收集关键点又是什么呢?很容易想到就是怎么判断哪些对象应该垃圾收集以及怎么做垃圾收集最快最省力。
[b]第一个关键点:怎么判断哪些垃圾收集?[/b]有人想到了一个简单实现,那就是引用计数法,如果引用一个对象就把这个对象的引用数+1,如果取消一个对象的引用就把这个对象的引用数-1。好了,如果引用数如果为0,那么我们说这个对象应该被垃圾收集。
当然,我想这个算法是很简洁易懂,但是并不完美。对于局部循环引用的的情况下,那两个对象会抱团存活(互相引用)。而这不是我们想要的。我们需要一并清理这些作假者。
基于如上问题,人们想到了另外一种实现,那就是根搜索算法,可以这么说,任何引用其他对象的引用都可以找到源头的一个对象,因此可以得出一个引用链,我们只要记录这个根引用的集合。所以,如果一个对象的任何引用追溯到根节点都不在根引用集合里面,那么即使这个对象即使被不是根引用的对象引用着也是可以被垃圾回收的。
[b]第二个关键点:怎么保证垃圾收集又快又省力?也就是高效[/b]
有人想到了如下算法,内存分成两半,一半用于工作内存,另外一半不用,这样,如果其中工作内存满了的时候就进行一次垃圾收集,收集的方式就是把这个满的一半中还存活的对象拷贝到另一半中就可以了,这个方法可以说是简单明了,速度很快,因为只是单纯的拷贝。
这个算法很高效但是不是很完美,首先很明显内存浪费了,因为有一半没有被利用。
于是人们想到了能否改变一下分配比例?比如不是1:1,而是8:2之类的。
据IBM调查发现新生代的对象是98%都是很快需要垃圾收集的。(证据需要考证)
因此人们把内存块划分为8(eden):1(survivor):1(survivor),这样平时用到eden区和一个survivor区。到内存满的时候拷贝到另外一个survivor里面,然后把其他的空间清理掉,然后进入下一个eden+survivor的工作模式。
上面两种算法都可以叫做mark-copy算法。
这似乎很好的解决了空间利用率的问题,但是我们看到有个前提就是对象98%需要垃圾收集。
对于那些需要垃圾收集的对象比例不高的情况下,这个复制算法就显得效率低下了。
因此人们想到了非全拷贝的算法。那就是mark-compact算法。
它的原理很简单,就是在垃圾收集的时候把还存活的对象往一端移动,尽量堆在一起,去掉内存碎片,然后把边界以外的内存空间清理掉就行了。
这个对于老生代或者持久代来说是很好的,因为,要拷贝的对象很少。所以代价最小,速度很高。
基于以上两种算法,现在商业jvm就把上述三种内存模式又细分为六种情况。
总体来说也就是三类:
串行收集(但CPU)
并行收集(多CPU)
并发收集(stop the world时间最小,实时性要求高)
[size=large][b]总结[/b][/size]
基于以上理解回过头来看看jvm的参数就很容易理解了。
[b]堆设置[/b]
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
[b]收集器设置[/b]
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
[b]垃圾回收统计信息[/b]
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
[i][u][b]并行收集器设置[/b][/u][/i]
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
[u][i][b]并发收集器设置[/b][/i][/u]
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。