• JVM GC与频繁GC


    内容:

    1.JVM GC的内存管理

    2.频繁 GC 的原因

    一、JVM GC的内存管理

    1.JVM内存分布图

    2.JVM GC运行原理

    (1)从对象角度来看

    在JVM进行GC时,内存中存在新生代(年轻代)、老年代(年老代)、永久带。

    在我们运行程序时,会创建很多对象。

    1)从生命周期上看:

    根据经验,绝大多数对象在使用过之后就不会在使用了。也就是说这些对象的使用的生命周期比较短。当然相应的,也有一少部分对象的生命周期会比较长。

    2)从对象大小上看

    在这些对象中会有很多小对象,和一些的大对象。

    因此,JVM在进行GC时就要从两方面来考虑GC问题。

    1)针对对象大小比较小且对象的生命周期比较短,会通过频繁GC把这些对象从内存中清除出去。

    2)对于对象大小比较大或者对象的生命周期比较长,一般不会通过频繁GC的方式,而是将这些对象放到老年代。

    (2)从JVM的内存空间来看JVM GC

    新生代有Eden(伊甸园)、From(Survivor 1)、To(Survivor 2)区域。

    Eden(伊甸园)主要用于存放新创建的对象,它会在第一次GC时,将对其内部的存活的对象放入到From或者To区域。但我们仍可以使用这些对象。

    From与To区域都是用Survivor区域,主要用于GC时,在对象复制时,提供作用,以及承担新生代与老年代之间的缓冲带的作用。

    就整个新生代来说,会进行频繁的GC。

    而老年代,则存放经过几次(一定次数)的GC仍存在的对象或者是大对象以及生命周期比较长的对象。

    GC的过程:

    当对象创建后会进入Eden中、垃圾收集器进行垃圾回收时,会扫描Eden以及From区域。如果对象仍然存活,即在进行GC时,对象仍存活。此时,就会将该对象由Eden、From复制到To区域。如果从Eden或Form区域进行对象复制操作时,To区域已经满了,则会直接将对象放入到老年代(Old Generation)。而在对象复制后,To区域中存在的对象就是有效的对象,此时会将To区域与From区域交换,然后将无效对象清除,即将From区域(此时已经经过交换而成为To区域)与Eden清空。所以我们说Old Generation是经过几次GC后仍存在的对象,其实最小的次数有可能是0次,这是因为如果对象特别大,就会在其创建时,直接将其放入到老年代;也有可能是1次,例如:当进行第一次GC时,要将Eden中的对象放入到To区域,但是此时To区域已经满了,那么对象就会直接放入到老年代了。在GC时,JVM还会对From区域中的对象进行扫描,发现进过几次GC仍然存在的对象,也会放入老年代中。然后再进行清空操作。

    从新生代GC的过程,我们可以看出年轻代的垃圾回收采用的是复制的方式进行。

    其次,Eden中的对象复制到老年代之前会用个缓冲地带(Survivor区域),Survivor缓冲地带主要是方便对象复制而设计的。采用复制进行对象的回收的好处是,不会产生内存碎片,此处采用了空间换时间的方式来加快垃圾回收过程。(这里需要提供两倍的内存空间,Survivor1和Survivor2。)

    对于老年代,其中放置的是进过几次GC仍然存在的对象,或者是大对象;老年代一般占用内存空间也较大,但是可以通过参数设置。相对而言,这部分不会进行频繁的GC(正常情况下,1h或者10h进行一次GC)。

    1.若在服务器端发生Full GC(老年代发生GC)对性能影响相当大,可能会造成服务器不响应的现象。

    2.Full GC:一般老年代和永久代发生GC,都为Full GC。

    在老年代中,对象可以采用压缩方式存储也可以不采用压缩方式存储,主要看具体的实现。

    对于永久代(PermanentGeneration)来说,其中存放的是类的定义,类的字节码,常量等,其中发生GC的时机或者条件:类的所有实例对象都被GC掉了,同时类的加载器也被回收掉了。

    二、频繁GC

    1.频繁GC的原因:

    (1)人为原因

    例如在代码中调用System#GC或者Runtime#GC方法。

    (2)框架原因

    在java程序调用相关框架时,框架内部调用了GC方法。

    (3)内存原因

    当heap大小设置比较小时,会引起频繁的GC,所以在类似于Spark这样对内存性能要求比较高的应用程序运行时,应可能给heap分配较大的内存,这样可以减少频繁的GC现象的发生。

    (4)其他原因

    当构建的对象实例化十分频繁并且释放该对象也较为频繁时,同样会产生频繁GC现象。

    & Scala是一种面向函数式和面向对象相结合的编程语言。从面向函数式编程风格来说,创建的对象一般都是不可变的。这样就给内存的管理带来了极大的好处,避免由于对象频繁的改变而造成的GC。同时,不可变对象也可以在代码中被其他函数复用。

    & 对用复用来说,是一把双刃剑。复用即通过缓存对对象进行使用。Cache级别的对象,若是其缓存命中很高时,此时应尽量复用这些对象。若cache命中率不高时,此时既占用内存,又会消耗IO、CPU等内存等资源。

    & 一般来说,进行GC时,保证heap 50%的剩余空间,相对来说是比较合适的。并且需要统计缓存命中率的问题,其命中率越高越好。

  • 相关阅读:
    【ELK】日志分析系统概述及部署(ELFK部署实验)
    Java Math.abs()如何获取绝对值呢?
    教你如何打造一款流行的服装定制小程序
    非零基础自学Java (老师:韩顺平) 第6章 数组、排序和查找 6.12 多维数组 - 二维数组 && 6.13 二维数组的使用
    深度解析Java JDK 1.8中Stream流的源码实现:带你探寻数据流的奥秘
    Mac:如何配置java和maven环境变量
    SQL之索引
    全屏组件封装(react18+antd)
    NX二次开发-C#使用DllImport调用libufun.dll里的UF函数(反编译.net.dll)调用loop等UF函数(三部曲1)
    【C++】类和对象(上),三种类对象模型,全局变量和静态变量在.h中的问题,类的内存对齐等等..快来看看
  • 原文地址:https://blog.csdn.net/m0_67402731/article/details/126409638