深入研究java gc

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入研究java gc相关的知识,希望对你有一定的参考价值。

2019/4/2 星期二
深入研究java gc
题外话:什么是java程序的执行流程;java运行时数据区;java的内存管理 见如下图:
java程序执行流程:
技术图片

java运行时数据区:
技术图片

java的内存管理:
技术图片

在我们(运行时数据区)之中,内存的分配一共有五块:
1、堆内存(Heap):保存真正的程序的数据的部分;
2、&&&栈内存(Stack):保存堆内存地址、还保存基本数据、方法的执行;(所有的数据都在栈内存之中)
3、方法区:保存所有方法的具体的操作,该区域属于共享;
4、程序计数器:这是一块很小的内存,小到几乎可以忽略的地步,只是做一个程序执行顺序的记录,只是为了标记我们下一步要执行的代码的顺序号;
5、本地方法栈:该栈之中所保存的都是操作系统的原生函数。
我们关心的主要是堆内存、栈内存、方法区

在整个的JVM内存组成过程之中,(栈内存)是一个非常重要的概念,因为在该内存之中,他需要保存的数据是一组内容,

因为所有的方法在进行递归调用的时候都会采用栈的模式。观察递归问题中满栈的原因取决于服务器内存的大小。

内存操作有关的两类异常
stackOverFlowError(栈溢出):如果请求的栈的深度过大,虚拟机可能会抛处。
OutOfMemoryError(内存溢出):如果虚拟机的实现中允许、虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出。【内存被沾满,更多情况下表示堆也分配不了了】

实际上上面只是观察到了两类可能出现错误的代码,但是并不是意味着栈中只能够保存一下基本的信息,实际上栈里面保存同样是一组的数据。

总结:
1、造成stackOverFlowError(栈溢出)OutOfMemoryError(内存溢出)的原因是;
2、在JVM栈内存中保存有栈幁的概念,所有的栈内存采用先进后出的数据结构来进行我们的存储。

首先需要了解一下什么是java的堆内内存划分
在实际情况下:java 堆内存划分分为了(jdk1.8以前和jdk1.8之后)【对于这2者的区别,我们后面介绍】
jvm堆内存划分(jdk1.8以前):
技术图片

jvm堆内存划分(jdk1.8之后):
技术图片

java堆内存模型
java的垃圾收集主要指的是java堆内存空间,那么在每一次执行GC的时候需要区分出那些堆内存空间需要被回收,那些不应该被回收。 所以为了整个的回收处理方便,JVM将堆内存分为如下的几个组成部分。而这几个组成部分你还需要去考虑JDK的版本,现在的JVM内存划分就必须考虑JDK1.8以前和JDK1.8之后的问题了。
如果简化点来理解的话:
1、新生代:那些刚刚创建的对象,刚刚创建的对象有可能会存在有许多垃圾对象,那么这些对象应该是被优先回收的;
2、老年代:老不死的那类对象,经过了很多次的清理之后你发现该对象依然有用,
3、永久代:intern()方法进行入池的对象实际上就在永久代中,永久代不会被回收。因为其本身属于一个bug性的存在(也就是jdk崩溃了,死了永久代CIA能消失),所以在jdk1.8之后,将其更换为元空间(就是电脑的直接内存)。
举个例子:我电脑有100G内存,80G给了堆内存,那剩下的20G就可以给元空间。

在整个内存的组成过程之中,每一代的内存空间都会有一个伸缩区,那么该区域就可以由JVM根据空间的使用情况,动态扩充。
当我们适当合理的设置了伸缩区的内存大小之后,那么就可以得到良好的性能提升。也就是说最容易的性能提升就是改变伸缩区的内存大小。

首先什么是java gc 、java对象创建流程
java对象创建流程如图:
技术图片

1、大多数内存对象要么生存周期比较短,很快就会没人引用,比如处理RPC请求的buffer可能只会生存几微秒;
2、要么生存周期比较长,比如Block Cache中的热点Block,可能就会生存几分钟,甚至更长时间。
3、基于这样的事实,JVM将整个堆内存分为两个部分:新生代(young generation)和老生代(tenured generation),除此之外,JVM还有一个非堆内存区-Perm区,主要存放class信息以及其他meta元信息,
4、其中Young区又分为Eden区和两个Survivor 区:S0和S1。
5、一个内存对象在创建之后,首先会为其在新生代申请一块内存空间,如果这个对象在新生代存活了很长时间,会将其迁移到老生代。
6、在大多数对延迟敏感的业务场景下(比如HBase),建议使用如下JVM参数,-XX:+UseParNewGC和XX:+UseConcMarkSweepGC,其中前者表示对新生代执行并行的垃圾回收机制,而后者表示对老生代执行并行标记-清除垃圾回收机制。
7、可见,JVM允许针对不同内存区执行不同的GC策略。


接下来重点先讨论一下年轻代

技术图片

年轻代GC实现复制算法:(年轻代GC策略 – Parallel New Collector)
技术图片

1、对象初始化之后会被放入Young区,更具体的话应该是Eden区,当Eden区满了之后,会进行一次GC。
2、GC算法会检查所有对象的引用情况,如果某个对象还有被引用,表示该对象存活。
3、检查完成之后,会将这些存活的对象移到S0区,并且回收整个Eden区空间,称为一次Minor GC;
4、接着新对象进来,又会放入Eden区,满了之后会检查S0和Eden区存活的对象,将所有存活的对象移到S1区,再回收整个S0和Eden区空间;
5、很容易理解,S0和S1两个区总会有一个区是预留给下次存放存活对象用的。

这种算法称为复制算法,对于这种算法,有两点需要关注:

  1. 算法会执行’stop-the-world’暂停,但时间非常短。因为Young区通常会设置的比较小(一般不建议不超过512M),而且JVM会启动大量线程并发执行,一次Minor GC一般都会在几毫秒内完成
  2. 不会产生碎片,每次GC之后都会将存活的对象放入连续的空间(S0或S1)
    内存中所有对象都会维护一个计数器,每次Minor GC移动一个对象之后,都会为这个对象的计数器加一。当计数器增加到一定阈值之后,算法就会认为该对象生命周期很长,会将其移入老生代。该阈值可以通过JVM参数XX:MaxTenuringThreshold指定。

提高了解篇
年轻代优化算法

技术图片

技术图片

技术图片

年轻代内存调整参数(重要):
技术图片


接下来深度研究老年代

什么是老年代 和老年代的full gc:
技术图片

以上是关于深入研究java gc的主要内容,如果未能解决你的问题,请参考以下文章

深入理解JVM-Java垃圾回收机制GC

我可以实施啥来深入学习.net GC

《深入理解JVM——如何优化Java GC「译」》

《深入理解JVM——如何优化Java GC「译」》

深入JVM虚拟机 Java GC收集器

译深入理解G1的GC日志