java 垃圾回收器 GC

3 篇文章 0 订阅
1 篇文章 0 订阅

java 垃圾回收器的一些理解

GC是啥?

GC垃圾回收:java语言的特点之一,不需要开发人员关心内存资源的释放,但是会增加软件系统的负担,算是有利有弊吧,但是选择合适的GC就会很明显利大于弊了。

先大致了解一下JVM内存区域:

JVM内存模型:

注意: 

1.java8之后取消永久带,设置了元空间(不占用虚拟机内存,而是机器本地内存),常量池被放到了方法区。

2.私有内存伴随着线程的产生而产生,一旦线程终止,私有内存区也会自动消除,所以垃圾回收不在此探讨范围内,着重看一下堆内存。

堆内存:

概念:所有的对象实例都要在堆上分配,无论死的还是活的对象。

作用:动态申请分配一定大小的内存空间

模型:

负责GC的垃圾回收器常用算法   

垃圾回收算法概述:

1.复制算法:

根节点扫描所有存活对象复制到新内存中,将原来那块内存回收掉。

 ----适合新生代

2.标记-清除算法:

两个阶段,标记阶段和清除阶段,从根节点开始,未标记的就是未被引用的垃圾对

象,清除阶段清除所有未被标记的对象。

3.标记-压缩算法

在标记-清除的基础上优化,一轮扫描后,先将所有存货对象压缩到内存另一端,之后清理边界外所有空间,可规避碎片的产生。

4.代回收算法:

不同年代使用不同算法,选择最合适的。新生代存活率低,可以用复制算法,而老年代存活率高,没有额外空间分配担保,用标记清除或者标记整理(压缩)。

新生代GC:

Minor GC垃圾回收,eden中存活对象会复制到未被使用的sur0中,正在使用的sur1的年轻对象也会被复制到sur0中(大、老对象进入老年代),如果sur0已满,对象也直接进入老年代,此时eden中和sur1的剩余对象就是垃圾对象,直接清空,而sur0中就是此次回收存活的所有对象,复制算法保证了内存的连续性,也避免了内存空间的浪费。最后sur0和sur1互换,保持sur0为空,如此反复,如果复制对象达到16次,那么这个对象就可以送去老年代了。

特点:频率高、速度快

Survivor的存在意义:减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。保证永远有一个survivor space是空的,另一个非空的survivor space无碎片。

老年代GC:

Major GC:经历16次的还存活就被送到老年代,老年代空间一般较大,使用标记-清除算法。扫描后回收,会产生内存碎片。标记-压缩算法可规避。

特点:是频率低速度慢也就是当老年代也放不下的时候发生FullGC,耗时严重。

System.gc()也会发生。

垃圾回收器

分类:

1.使用串行回收器的相关参数:

-XX:+PrintGCDetails:打印GC信息。

-XX:+UseSerialGC:新生代、老年代都使用串行回收器。

-XX:+UseParNewGC :新生代使用并行收集器,老年代使用串行收集器。

2.使用并行回收器的相关参数:

-XX:+UseParNewGC:新生代使用并行收集器,老年代使用串行收集器。

-XX:ParallelGCThreads :并行收集器工作时的线程数量。

-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。

-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。

-XX:+MaxGCPauseMills:设置最大垃圾收集停顿时间,它的值是一个大于 0 的整数。

-XX:+GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。

3.使用CMS回收器相关参数:

-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。

-XX:CMSInitiatingOccupancyFraction:指定回收阈值,默认是 68。

-XX:+UseCMSCompactAtFullCollection: CMS 在垃圾收集完成后,进行一次内存碎片整理。内存碎片的整理并不是并发进行的。

-XX:CMSFullGCsBeforeCompaction:设定进行多少次 CMS 回收后,进行一次内存压缩。

4.使用G1回收器相关参数:

-XX:+UnlockExperimentalVMOptions:允许使用实验性参数。

–XX:+UseG1GC:启用 G1 回收器.

-XX:MaxGCPauseMills:设置 G1 回收器的最大停顿时间。

-XX:GCPauseIntervalMills:设置 G1 回收器的目标停顿时间间隔。

标记清除 (mark sweep) 详解:

这里为简单化,我们把变量使用的每一块内存都叫对象。我们知道,栈上的对象在函数调用过程中或者调用结束后全部被释放掉,可以理解为这是操作系统的ABI规 范,因此我们不需要管理栈内存,垃圾回收器管理的是堆内存。那垃圾回收器怎么知道哪些对象可以回收呢?应该是那些从栈上无法直接或间接访问到的堆上的对象可以被回收。那又怎么判定从栈上无法直接或间接访问到某个堆上对象呢?这也是我以前比较困惑的地方。比如我在某个函数里边写了一行:“A* p = new A();”,在A对象产生的时候,应该在某个地方有个记录,保存了p的值。在函数退出时,记录里p的值被清理,但是它所指向的内存对象还存在,等到垃圾回收器进行回收检查的时候,它先要把能从栈上访问到的内存对象标记为被使用中,以免清理阶段错误释放了该内存对象。对于p,因为在记录里被清理了,在标记阶段,从栈上已经无法访问它,因此没有被标记为使用中,在清理阶段这个对象就会被释放掉。那么问题又来了,既然从栈上已经无法访问到p指向的对象,又如何释放呢?其实是有另外一个路径可以访问到它的,垃圾回收器保存一个根对象,通过这个根对象可以访问到所有已分配的内存对象,但这些对象可能已经没有栈上变量 指向它了,垃圾回收器正是以这个为依据来进行内存释放。

在作者的实现中,有两个结构体比较重要,就是虚拟机结构体,声明如下:

typedef struct {

    Object* stack[STACK_MAX];
    int stackSize;
 
   /* The first object in the linked list of all objects on the heap. */
   Object* firstObject;
 
   /* The total number of currently allocated objects. */
   int numObjects;
 
   /* The number of objects required to trigger a GC. */
  int maxObjects;
 } VM;
 

从栈上能访问到的对象是那些从VM结构体中stack变量可以直接或间接访问到的对象,这里的对象就是Object类型的,Object声明如下:

typedef struct sObject {
    ObjectType type;
    unsigned char marked;

    /* The next object in the linked list of heap allocated objects. */
   struct sObject* next;
 
   union {
     /* OBJ_INT */
     int value;
 
     /* OBJ_PAIR */
     struct {
       struct sObject* head;
       struct sObject* tail;
     };
   };

} Object;

Object结构体里有个next成员指向下一个对象,因此有间接访问到这么一说法。

VM结构体中stack变量决定了哪些对象还在被引用,比如上面提到的p,在刚刚给它分配对象时,从stack可以访问到它,但是当它离开所在作用域又没有其它变量引用过p所指向的内存对象时,从stack变量就已经无法访问到它了。但在p所指向内存对象被垃圾回收器释放之前,从VM结构体里的firstObject一步一步往下走,始终能找到这个内存对象。

顾名思义,mark-sweep算法分为两个阶段,即mark和sweep阶段,中文就是标记和清理阶段。有了以上的理解之后,我们就明白,这个实现中为什么在调用函数gc时依次调用了markAll(vm)和sweep(vm)。

void gc(VM* vm) {
   int numObjects = vm->numObjects;
 
   markAll(vm);
   sweep(vm);

    vm->maxObjects = vm->numObjects * 2;

    printf("\nCollected %d objects, %d remaining.\n", numObjects - vm->numObjects,
          vm->numObjects);
 }

每个Object对象有一个标记位,在这个对象初始化时标记为置0,表示可以被回收(是不是显得有点奇怪,会不会被错误释放?)。我刚开始直觉上也感觉不对,新对象不是在被使用吗,应该为1才对啊。而实际其思路是这样的:首先认为所有对象都可以被回收,然后在你回收之前,遍历stack变量,把从它能到达的对象都标记为1,这就是标记阶段,再执行清理,清理不再是遍历stack变量,而是遍历firstObject变量。对于那些遍历stack变量时因为没有直接或间接引用而不可达的变量,在清理阶段遍历firstObject却能找到它,并发现它的标记为是0,于是把它释放掉。对于那些标记为1的变量,把它重新置0回到初始状态,表示可回收,等待下次gc调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载的分类、范围、双亲委托策略,自己手写类加载,理解字节码解释、即时编译、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收的原理和使用,垃圾回收的组合及分代收集等。第8章 GC日志详解,各种垃圾回收的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值