JVM 内存管理

阅读更多
JVM 内存管理

JVM 内存管理_第1张图片

//============================================
年轻代(Young Generation)
年轻代由Eden Space和两块相同大小的Survivor Space(又称S0和S1)构成

可通过-Xmn参数来调整新生代大小,也可通过-XX:SurvivorRadio来调整Eden Space和Survivor Space大小。

Eden区采用的复制回收算法

对象在被创建时,内存首先是在年轻代进行分配

-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold
用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1。

过程:
对象新建时在Eden的(一些大对象会直接在老年区中新建),当Eden满时就会进行Minor GC,Minor GC会将Eden中还活着的对象复制到S1,同时Minor GC还会将S0中还活着的复制到S1,当然S0中有些对象可能经过多次都没有被回收,就直接复制到年老区中去了。

清空Eden和S0,把S0变为S1,S1变为S0,等待下一次的Minor GC.

当S1满了就会将S1所有对象放到年老区中去,所以这里如果太小,就会将一些对象过早的复制到老年区中,引起内存泄漏

年轻代:涉及了复制算法;

Minor GC都会触发全世界的暂停(stop-the-world),停止应用程序的线程,不过这个过程非常短暂。

新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。(就是大到一定程度就会触发回收)




//============================================
老年代(Old Generation)
超过有设定次数Minor GC后还存在的对象会在这个区中,大的对象也会在这个区中,

老年区中的对象如果没有用了会在Full GC中进行删除,Full GC运行频率低

年老代:涉及了“标记-整理(Mark-Sweep)”的算法。


//============================================
Permanate generation(主要存放一些类的元信息,不在包含在堆中,独立的一块内存)
(Permanate generation)如,class对象、方法对象这些可反射(reflective)对象分配内存限制,这些内存不包括在Heap(堆内存)区之中。
Java 8以后移除了方法区,取而代之的是本地元空间Metaspace,大小由-XX:MetaspaceSize和-XX:MaxMetaspaceSize调节。



//============================================
分代回收的唯一理由就是优化GC性能。不用一次进行全部回收处理

每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对会很长


//============================================
内存的回收方式
引用计数法:
对象被引用计数加1,不再引用计数减1,计数为0就表示可以回收了,互相引用这个方法不能解决



标记-清除(Mark-sweep):
主要就是两个动作,一个是标记,另一个就是清除。

标记就是根据特定的算法(如:引用计数算法,可达性分析算法等)标出内存中哪些对象可以回收,哪些对象还要继续用。

回收,那就直接收掉

不足:效率低;标记清除之后会产生大量碎片。

JVM 内存管理_第2张图片



复制(Copying):
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。

不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。

JVM 内存管理_第3张图片



标记-整理(Mark-Compact):
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。就是在本区域内进行整理复制

JVM 内存管理_第4张图片




//============================================
在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew):

串行GC:(应用停下)
在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定。


并行回收GC:(应用停下)
在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数。


并发GC(CMS)(响应比并行gc快很多,但是牺牲了一定的吞吐量)(应用和GC一起运行,但微观上应用还是停下的)
使用CMS是为了减少GC执行时的停顿时间,垃圾回收线程和应用线程同时执行,可以使用-XX:+UseConcMarkSweepGC=指定使用,后边接等号指定并发线程数。CMS每次回收只停顿很短的时间,分别在开始的时候(Initial Marking),和中间(Final Marking)的时候,第二次时间略长。CMS一个比较大的问题是碎片和浮动垃圾问题(Floating Gabage)。碎片是由于CMS默认不对内存进行Compact所致,可以通过-XX:+UseCMSCompactAtFullCollection。

CMS收集器是基于标记-清除算法的实现,因此也会产生碎片。


G1收集器:
G1是一款面向服务端应用的垃圾收集器。

与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,


G1 GC还不是默认的jvm gc策略(目前为止),需要使用的话可以加入以下参数开启:
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC        #开启
-XX:MaxGCPauseMillis =50                  #暂停时间(毫秒)
-XX:GCPauseIntervalMillis =200          #暂停间隔(毫秒)
-XX:+G1YoungGenSize=512m            #年轻代大小
-XX:SurvivorRatio=6                            #幸存区比例



//============================================
常见配置汇总
堆设置
-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:设置持久代大小
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。


//============================================
参数优化
Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍
永久代 PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为老年代存活对象的1-1.5倍。
老年代的内存大小设置为老年代存活对象的2-3倍。

1、Sun官方建议年轻代的大小为整个堆的3/8左右, 所以按照上述设置的方式,基本符合Sun的建议。
2、堆大小=年轻代大小+年老代大小, 即xmx=xmn+老年代大小 。 Permsize不影响堆大小。

//============================================
JVM调优工具
Jconsole,jProfile,VisualVM
Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。详细说明参考这里
JProfiler:商业软件,需要付费。功能强大。详细说明参考这里
VisualVM:JDK自带,功能强大,与JProfiler类似。推荐。


//============================================
年轻代:涉及了复制算法;年老代:涉及了“标记-整理(Mark-Sweep)”的算法。









https://blog.csdn.net/tonytfjing/article/details/44278233(JVM结构、GC工作机制详解)

https://blog.csdn.net/lijunwyf/article/details/52605696?utm_source=blogxgwz3(垃圾收集器Serial 、Parallel、CMS、G1)

https://blog.csdn.net/aijiudu/article/details/72991993?utm_source=blogxgwz0(JVM架构和GC垃圾回收机制)

https://blog.csdn.net/YHYR_YCY/article/details/52566105?utm_source=blogxgwz0(GC详解及Minor GC和Full GC触发条件总结)


https://blog.csdn.net/ppby2002/article/details/6096837(java jvm 参数 -Xms -Xmx -Xmn -Xss 调优总结)

https://www.cnblogs.com/softidea/p/4043804.html(JVM虚拟机选项:Xms Xmx PermSize MaxPermSize区别)



触发主GC(Garbage Collector)的条件
1.当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

2.Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。


触发主full GC(Garbage Collector)的条件
1.空间不足
2.系统调用触发
3.默认情况下会一小时执行一次Full GC,可通过在启动时通过- java -
Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。













  • JVM 内存管理_第5张图片
  • 大小: 24.4 KB
  • JVM 内存管理_第6张图片
  • 大小: 13 KB
  • JVM 内存管理_第7张图片
  • 大小: 14.3 KB
  • JVM 内存管理_第8张图片
  • 大小: 14.6 KB
  • 查看图片附件

你可能感兴趣的:(JVM 内存管理)