JAVA垃圾回收机制及JVM调优

在这里插入图片描述
部分资源来自网络收集整理,如有侵权请联系删除

什么是垃圾回收机制?

不定时地回收堆内存中不可达的对象。【Java中作用域针对于栈而言的,栈是一个先进后出的数据结构,通常保存方法中的参数和局部变量。堆是共享区域,所以不存在作用域的说法。那么栈需要垃圾回收吗?因为栈的空间很小,只存放对堆的引用和基本类型,在方法执行完毕后会被清除】

在java语言中,可作为GC Root的对象包括以下几种对象:
a. java虚拟机栈(栈帧中的本地变量表)中的引用的对象。
b.方法区中的类静态属性引用的对象。
c.方法区中的常量引用的对象。
d.本地方法栈中JNI本地方法的引用对象。

java方法区在Sun HotSpot虚拟机中被称为永久代,很多人认为该部分的内存是不用回收的,java虚拟机规范也没有对该部分内存的垃圾收集做规定,但是方法区中的废弃常量和无用的类还是需要回收以保证永久代不会发生内存溢出。
判断废弃常量的方法:如果常量池中的某个常量没有被任何引用所引用,则该常量是废弃常量。

判断无用的类:
(1).该类的所有实例都已经被回收,即java堆中不存在该类的实例对象。
(2).加载该类的类加载器已经被回收。
(3).该类所对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射机制访问该类的方法。

Java中常用的垃圾收集算法:

(1).标记-清除算法:
最基础的垃圾收集算法,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。
标记-清除算法的缺点有两个:首先,效率问题,标记和清除效率都不高。其次,标记清除之后会产生大量的不连续的内存碎片,空间碎片太多会导致当程序需要为较大对象分配内存时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
(2).复制算法:
将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
复制算法的缺点显而易见,可使用的内存降为原来一半。
(3).标记-整理算法:
标记-整理算法在标记-清除算法基础上做了改进,标记阶段是相同的标记出所有需要回收的对象,在标记完成之后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,在移动过程中清理掉可回收的对象,这个过程叫做整理。
标记-整理算法相比标记-清除算法的优点是内存被整理以后不会产生大量不连续内存碎片问题。
复制算法在对象存活率高的情况下就要执行较多的复制操作,效率将会变低,而在对象存活率高的情况下使用标记-整理算法效率会大大提高。
(4).分代收集算法:
根据内存中对象的存活周期不同,将内存划分为几块,java的虚拟机中一般把内存划分为新生代和年老代,当新创建对象时一般在新生代中分配内存空间,当新生代垃圾收集器回收几次之后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中无法找到足够的连续内存时也直接在年老代中创建。分为新生代与老年代就是为了使垃圾回收次数在老年代中尽量少,新生代中多一点。

以上部分资源来自空谷幽澜的深入理解java垃圾回收算法

什么情况下触发GC?

Minor GC:当Eden区中没有足够空间进行分配时触发。
Major GC/Full GC:
1.调用System.gc()方法会建议JVM进行Major GC,因为是建议并不一定会进行,但是大多数情况下还是会进行Major GC,强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。
2.老年代空间不足,JVM会进行Major GC,如果Major GC完后空间还是不足,就会抛出java.lang.OutOfMemoryError: Java heap space异常
3.方法区空间不足,JVM会进行Major GC,如果Major GC完后空间还是不足,就会抛出java.lang.OutOfMemoryError: PermGen space异常
4.java虚拟机中有担保机制,当新生代的复制空间存储不下存活对象时就会触发担保机制,会把剩下无法存储的对象直接存放进老年代,如果此时老年代空间也无法存储,就会担保失败,担保失败后JVM会进行Major GC
5.CMS进行GC的时候会产生浮动垃圾,浮动垃圾就是进行GC过程中产生的垃圾,占用了空间,没有被GC,导致空间不足,JVM虚拟机进行Major GC
6.分配的对象太大,这种对象会直接进入老年代,老年代的剩余空间够,但是连续空间不够,此时JVM虚拟机进行Major GC

原文参考虚拟机的GC触发条件

jvm参数如何设置?

升级JDK1.8之后,原perm(永久代)替换成了Metaspace(元空间)
MetaspaceSize为出发FullGC的阈值,默认约为21M,如做了配置,最小阈值为自定义配置大小。空间使用达到阈值,触发FullGC,同时对该值扩大。当然如果元空间实际使用小于阈值,在GC的时候也会对该值缩小。
MaxMetaspaceSize为元空间的最大值,如果设置太小,可能会导致频繁FullGC,甚至OOM。
这里需要对jvm参数有一定了解,可跳转至JVM参数配置指南学习。
Java堆大小设置,Xms 和 Xmx设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍
元空间设置为老年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为老年代存活对象的1-1.5倍。
老年代的内存大小设置为老年代存活对象的2-3倍。

老年代存活对象如何获取?

在jdk安装bin目录下有许多exe文件,其中可使用jstat查看gc的情况,工具使用教程可跳转至Jstat命令详解学习,除此之外的检测调优工具还有Jconsole,jProfile,VisualVM等
gc状态
这里的OU就是老年代占用的大小19727.6kb

疑问:以上设置依据是否可靠?
博主有个项目执行Jstat结果如图:
gc结果状态
从这里看到的OU为510kb,但是很明显根据上述配置参数的话项目是无法正常启动的。先在这里留一个疑问。
其它相关文章:JVM调优参数设置JVM参数的含义

回收器如何选择?

JVM给了三种选择:串行收集器,并行收集器,并发收集器。默认情况下,JDK5以前是使用的串行收集器,如果想使用其他收集器需要在启动时加入相应的参数,JDK5以后,JVM会根据系统当前的配置进行判断。
吞吐量优先的并行收集器:

-XX:+UseParallelGC:选择垃圾收集器为并行收集器。次配置仅对年轻代有效。即上述配置下,年轻代使用并行收集,而年老代仍旧使用串行收集。
-XX:PARALLELgcThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相同。
-XX:+UseParallelOldGC:配置年老代来及收集方式为并行收集,JDK6.0支持对年老代并行收集
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
-XX:+UseAdaptiveSizePolicy:设置此选项以后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开

响应时间优先的并发收集器:

-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理、所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

【多提一点】内存溢出与内存泄漏?
内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
内存溢出指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

【多提一点】JVM内存结构和Java内存模型
推荐一篇个人觉得很好的文章JVM内存结构和Java内存模型
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。 程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题。不会直接传对象本身。

若以上论述有不对的地方还请各位大佬批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值