JVM内存优化

一、JVM内存模型及垃圾收集算法

1.根据Java虚拟机规范,JVM将内存划分为:

  • New(年轻代)
  • Tenured(年老代)
  • 永久代(Perm)

其中New和Tenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。

年轻代(New):年轻代用来存放JVM刚分配的Java对象
年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。
New又分为几个部分:

Eden:Eden用来存放JVM刚分配的对象
Survivor1
Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

二.垃圾收集算法

1.标记清除算法(Mark-Sweep)
  算法分为两个阶段,标记与清除。

标记阶段:标记出所有需要回收的对象。回收阶段:将所有标记区域回收。由于该算法不对空间进行整理,因此会产生大量的内存碎片,内存空间碎片过多会导致在分配较大的对象时,因为没有连续的内存而不得不提前触发一个GC。另外,标记与清除的过程效率都不高。这也是最基础的GC算法。

2.复制算法(Copying)
  将内存的总容量分为两块,每次只使用其中的一块,当这一块用完了,触发GC,此时将还存活的对象转移到另一块内存中,之前使用的那一块内存完全清理掉。这样每次对一个半区进行回收,也不会存在内存碎片,实现简单,运行高效,但是一次只能使用半块内存可能会造成浪费。

在新生代中,绝大部分的对象时“朝生夕死”的,因此,不需要按照1:1来划分空间。而是将内存分为一块较大的Eden区以及两个Survivor区,HotSpot虚拟机中,Eden:Survivor=8:1 ,每次使用一个Eden区以及一个Survivor区,90%的空间,触发GC后,将剩余的对象转移到未使用的Survivor中,然后清理Eden区和用过的Survivor区,空间不够时,会担保分配到老年代。这样一次可以使用90%的内存空间,极大的提高了内存的使用率。因此,新生代一般采用这种算法来回收。

3.标记整理算法(Mark-Compact)
  如果回收时空间内的对象存活率较高,那么使用复制算法一次只能使用50%的空间(以应对所有对象都存活的情况),因此老年代采用标记整理算法。先对需要清理的对象进行标记,然后将存活的对象都向一端移动,直接清理掉端边界以外的内存。这种方式也不会留下内存碎片。

标记整理算法没有复制算法快。

三、为什么崩溃前垃圾回收的时间越来越长?

A:根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

Q:为什么Full GC的次数越来越多?

A:因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

Q:为什么年老代占用的内存越来越大?

A:因为年轻代的内存无法被回收,越来越多地被Copy到年老代

四、性能调优

在CPU负载不足的同时,偶尔会有用户反映请求的时间过长,我们意识到必须对程序及JVM进行调优。从以下几个方面进行:

  • 线程池:解决用户响应时间长的问题 (java.util.concurrent.ThreadPoolExecutor)
  • 连接池
    (org.apache.commons.dbcp.BasicDataSource)
  • JVM启动参数:调整各代的内存比例和垃圾回收算法,提高吞吐量
  • 程序算法:改进程序逻辑算法提高性能

针对JVM堆内存参数优化

堆内存是Java进程的重要组成部分,几乎所有与应用相关的内存空间都和堆有关。现在主要介绍与堆内存相关的参数设置,这些参数对Java虚拟机中非常重要的,也是对程序性能有着重要的影响。让你彻底脱离OOM内存溢出等等带来的程序崩溃。

1、 -Xms 初始堆内存大小,当Java进程启动时,虚拟机会分配一块初始堆空间,可以使用-Xms指定这块空间大小。在实际工作中,我们一般把-Xms与-Xmx的值设置为相等,这样的好处是在程序运行时减少GC的次数,从而提高程序性能。

2、 -Xmx 当程序在运行时,堆初始空间消耗殆尽,虚拟机会对堆空间进行扩展,其扩展上限是最大堆空间,使用-Xmx参数来指定。(方便大家好记住,这里mx可以暂时理解为max最大)

3、-Xmn 此参数是用来指定新生代的大小(堆内存是分为新生代, 老年代,永久带-在jdk1.8后移除此属性),新生代分为Eden、from、to空间。设置一个较大新生代会降低老年代的大小,这个参数设置对系统性能已经GC行为有极大的影响。 新生代一般设置为堆内存的1/3-1/4左右。

4、-XX:SurvivorRatio 用来设置新生代中Eden空间和from/to空间的比例关系,表达式如下:-XX:SurvivorRatio=eden/from=eden/to。

5、-XX:NewRatio 可以设定老年代与新生代的比例。

———————————————————————————————————
参考:https://blog.csdn.net/u013381397/article/details/71080535
https://www.cnblogs.com/malihe/p/7359893.html
https://www.cnblogs.com/csniper/p/5592593.html
侵删

猜你喜欢

转载自blog.csdn.net/weixin_41349389/article/details/84990772
今日推荐