JVM内存模型,GC机制和原理

  

JVM内存模型是Java在运行的时候Java虚拟机在拿到了自己可以支配的内存之后把内存进行了割分。

本地方法栈:存储C++运行时候用到的Native方法时候的栈区

程序计数器:指向当前程序运行的位置

 

栈区:用来存储程序运行过程中的一些临时变量

而这三部分合在一起是线程私有的,什么是线程私有?

线程私有就是每个线程在开辟和运行的过程中会单独创建一份这样的内存,就比如有多少个线程就会有多少个栈区

堆区:主要用来存对象(栈区存储的对象实际上是对象的引用类型,就是存储的是对象的地址,最终是指向堆区的实际存在的对象的)

方法区:存储一些元数据信息,在jdk1.7之前又叫做永久代,jdk1.8之后改名为元数据空间;主要用来存储一些静态方法或者变量,类加载器classloader等全局信息,这里面的数据是全局共享的

堆区和方法区是线程共享的

 

下面我们来看一行代码

在这段代码的func1函数中

首先形参a会在栈区申请一个空间 a = 10

然后b也是跟a一样申请一个空间在栈区 b = 10

Person p = new Person();

new是开辟内存空间的关键字,于是会在堆区开辟一块内存,存储一个person对象,这个对象分为两部分

在初始化的过程中id默认为0,name默认为null

与此同时 P在栈区也创建了一个指针(存储地址)指向堆中的这个对象

然后p.id,p根据地址在堆中找到了这个内存把他的id设置为1

而到p.name的时候,会直接在堆内存中开辟内存创建一个String对象,存储jk,而String对象也有一个地址,就是被p.name指向的地址(注意:p.name = "jk" 这种方式会以字符串的形式存储在方法区的常量池中)

然后当程序运行完毕后,栈区上存储的东西被清空了,那么堆区里的内容怎么办呢,

这就要靠我们的GC垃圾回收机制了

 

堆区是用来保存我们new出来的对象的,当不断地有对象new出来,堆区早晚会装满, 而装满后我们的堆内存就会爆掉,程序会运行失败,显示这是不允许的;那么如何进行GC呢?

1.首先判断这个对象是否需要被删除,判断的标准就是GCRoot,GCRoot一般指的是堆中被栈或者方法区中直接引用或者间接引用的这些对象是不能被删除的,原因就是可能这些在堆中的对象我们还在用,你当然不能给我删掉或者清理掉

2.清理堆区对象的思路:

①把不想删除的对象打上标记,将其记录为可达对象,当到了清除的时候就清楚哪些没有带标记的内存,这就是标记清除算法,但是这个算法有缺点,会出现内存碎片,当同时释放了两个1K的内存的时候,有一个2K的对象想存储到这里,是不能存储的,那么这两个1K的内存空间就不能被合理的利用起来了,如何解决?于是提出了另外一种算法

②标记整理算法:清理完之后后面的内存需要往前补充,以防止产碎片,缺点是代价很大,因为每个内存在清理后如果需要就要往前移动

③复制算法:将整个内存一分为二分为一区和二区,当在一区的对象一直创建内存快满了需要清除的时候,将有标记的内存(就是不被清除的内存)复制到二区,而且是紧凑的复制这样即避免了碎片问题而且开销也不大,但缺点也显而易见,需要两倍的内存

3.实际的GC过程

将堆区进行划分,一部分叫做"年轻代",另一部分叫做"老年代",而年轻代又进行了划分,三部分,一个Eden区,两个Survivor区(这里假设是S0  和 S1  区)

,old区(老年代)只有一块;

当我们在new对象的时候,先到达Eden区,当E区快要满了的时候会触发GC(年轻代这个区域的GC,又叫youngGC),采用的是复制算法,将被标记的对象(幸存对象复制)到S0区,然后将E区和S1区的对象全部删除,然后等下一次E区快要满了的时候,被标记的对象复制到S1区,将S0和E的对象全部删除,交替使用

而每次youngGC的时候对象的年龄都会+1,直到这个对象到了6岁,那么他就不去S区了,他就会直接到Old区中,Old区中也会保存一些很大的对象,当old去也快满了会产生oldGC,这个GC的同时也会伴随youngGC,所以说又叫fullGC,fullGC会引起STW(Stop The world)就是整个Java程序会暂停,会全力进行垃圾回收(这个回收主要使用的是标记清理和标记整理算法)。

4.说一下几个比较有名的垃圾收集器

年轻代的:ParNew,复制算法

老年代:CMS,标记清理算法

当然新版的JDK不建议使用这些老的垃圾收集器了,而是采用了G1垃圾收集器

G1垃圾收集器

首先垃圾收集器也是分代的,有

年轻代:Serial(串行的垃圾收集器),复制算法,单线程,效率比较低;ParNew(Serial的多线程版本),复制算法;Parallel Scavenge,简称PS垃圾收集器,它主要关注的点是吞吐量(一段时间内能持续提供服务的时间,一般客户端会比较关注吞吐量),因此它不关注单次GC的时间

老年代:Serial Old,Parallel Old,分别与年轻代的Serial和PS绑定使用;还有一个著名的服务端的垃圾收集器CMS(一般与ParNew组合使用),专注于单次垃圾收集的时间,最大限度的降低了单次垃圾收集的时间

CMS执行的主要步骤:

1.初次标记,会标记出GCRoot直接引用的对象,这个过程会STW

2.并发标记,不会STW,标记所有的old对象

3.重新标记,修正第二步,会STW

4.并发清理 ,标记清理算法,清除未被标记的对象

G1垃圾收集器的内存结构:

G1的堆内存是这样的

堆内存被划分为棋盘状

这里面每个regin(每个方块),大小都在1M~32M之间并且是2的幂次方,总的regin个数大概是2000个左右

声明对象的规则:

如果对象P

0.5 regin <= P < 1 regin,那么这个对象会存到old区,而且这个old区会被标记为H区;

P > 1 regin,会申请多个连续的H区

下面说一下两个概念:

RememberSet(Rset) :每个regin都会有一块区域专门存储Rset,它的作用是记录其他的regin是否引用了当前regin对象

CollectionSet(Cset):本次GC需要清理的regin的集合

G1垃圾收集器的GC:

年轻代的GC(YGC):

从图中我们可以看见,绿色的区域属于年轻代的E区和S0区,在一次GC的过程中会把E和S0复制到S1区,而S1区在G1中没有提前标记出来,在拷贝之后深绿色区域就是S1区。

在G1中没有单独针对old区的GC,在GC old区的时候也GC了young区,所以叫MixGC

1.初次标记,标记GCRoot对象,还标记了这些对象所在的regin区,这些区域也叫RootRegin,也会STW

2.将RootReign拿出来去扫描old区的所有reign,看看它们的rset中是否有rootregin,如果有就标识出来,标识的作用是为了第三步

3.并发标记,跟CMS相同,遍历所有的对象,看看对象是否能连接到GCRoot,如果连接了就不需要被清除,在G1中只不过遍历的范围缩小了,只需要遍历被标记的对象,看看其中有没有没有连接到GCRoot的,没有被标记的地方可以直接清除

4.重新标记,同CMS,只不过G1中使用了SATB,过程也会STW

5.复制清理,会STW,只会选择垃圾较多的regin进行清理,虽然清理不干净,但清理时间很短

JVM调优

 请注意,jvm调优,调的是稳定,并不能带给你性能的大幅提升。服务稳定的重要性就不用多说了,保证服务的稳定,gc永远会是Java程序员需要考虑的不稳定因素之一。复杂和高并发下的服务,必须保证每次gc不会出现性能下降,各种性能指标不会出现波动,gc回收规律而且干净,找到合适的jvm设置。

调优的原则/目标:

从JVM调优的角度来看,我们应该尽量避免发生YGC或FullGC,或者使得YGC和FullGC的时间足够的短。

调优的方法:

1、将新对象预留在新生代

由于 Full GC 的成本要远远高于 Minor GC ,因此尽可能将对象分配在新生代可以为应用程序分配一个合理的新生代空间,以最大限度避免新对象直接进去老年代。

2、大对象进入老年代

使用参数 -XX:PretenureSizeThreshold 设置大对象直接进入老年代的阀值,当对象超过这个阀值时,将直接在老年代中分配。其中, -XX:PretenureSizeThreshold 只对串行收集器和新生代并行收集器有效,并行回收收集器不识别这个参数。

3、设置对象进入老年代的年龄

-XX:MaxTenuringThreshold:默认值是15,这个参数是指定进入老年代的最大年龄值,对象实际进入老年代的年龄是 JVM 在运行时根据内存使用情况动态计算的。

4、稳定与震荡的堆大小

-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认是 40 ,当堆空间的空闲比例小于这个值时,JVM 便会扩展堆空间

-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例,默认是 70,当堆空间的空闲比例大于这个值时,JVM 便会压缩堆空间,得到一个较小的堆

5、吞吐量优先设置

6、使用大页案例

使用大的内存分页可以增强 CPU 的内存寻址能力,从而提高系统的性能,参数设置如下:

-XX:LargePageSizeInBytes:设置大页的大小

7、降低停顿案例

为了降低应用软件在垃圾回收时的停顿,首先考虑的使用关注系统停顿的 CMS 回收器,为了减少 Full GC 的次数,应尽可能将对象预留在新生代,新生代 Minor GC 的成本远远小于老年代的 Full GC

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值