java虚拟机——垃圾回收机制
问题1:什么是垃圾回收机制?
在java的虚拟机当中,在我们进行实例化的时候,堆会给我们开辟新的空间存放实例。而由于堆,方法区是线程公有,不会像栈区(线程私有)一样随着线程的销毁而销毁。因此在java虚拟机中必须要有垃圾回收的机制,定时清理内存,防止内存溢出(OutMemory)的情况。
问题2:哪些运行时数据区中的哪些内存需要被GC?
在运行时数据区中,分别存在以下的区域:
虚拟机栈,本地方法栈中的内存会随着线程的销毁而清空,而方法区和堆不会自动情况这是垃圾收集器所关注的部分,因此需要JVM进行GC。
问题3:如何判断哪些内存需要被GC?
1、引用计数算法。
当创建对象实例时候,就会给该变量的实例创建一个变量(计数器),初始值为1。当其他变量用这个对象进行赋值的时候,这个对象的变量就会+1。当这个对象过了生命周期或者赋了新的值后,该计数器就会减1.当计数器的值为0,该对象也就会被回收。
优点:对线程的运行影响不大,而且执行快。
缺点:无法检测循环的引用。如父对象引用子对象,子对象引用父对象。这种情况下计数器不可能为0,也不可能被回收。
/*虚拟机参数:-verbose:gc*/
public class GCDemo { private Object instance = null; public static void main(String[] args) { GCDemo gcDemoParent = new GCDemo();
GCDemo gcDemoChild = new GCDemo();
gcDemoParent.instance = gcDemoChild;
gcDemoChild.instance = gcDemoParent; gcDemoChild = null;
gcDemoParent = null;
System.gc();
}
}
以上的代码可以看出父子互相调用,且执行了System.gc()。
输出结果:
[Full GC (System.gc()) 1216K->560K(15872K), 0.0165474 secs]
1216K代表执行之前的内存,560代表执行GC后的内存,15872代表虚拟机的内存,最后的代表执行时间。通过输出结果可以得知,该虚拟机不是通过引用计数算法来判断对象是否存活。
2、可达性算法:
在我学习的过程中,只知道引用计数算法和可达性算法两种算法判定对象是否存活。
这个图很好的阐释了可达性算法的算法思路。他就像一颗树,不断的进行引用,从GC Root(根集合)开始,不断的通过引用链进行引用。当有对象没有被引用链的时候,就会出现对象不可达的情况,此时就代表对象是不可用的(ObjD Obje)。
什么可以作为GC Root呢。从网上的资料查找得出,GC的对象包括:
1、虚拟机栈中的引用对象(栈帧中的本地变量表)。
2、方法区中类静态引用的对象。
3、方法区中常量引用对象。
4、本地方法栈中的引用对象。
第一次标记:在对象被发现没有被引用时,会被标记第一次,并不会立刻执行GC。
第二次标记:在对象被标记后进行筛选,看该对象是否有必要执行finalize()方法(拯救自己机会只有一次,如将自己赋值给其他对象),若在该方法中也没有进行连接,则就要挂了。
public class GCDemo { static GCDemo gcDemo = null; protected void finalize() throws Throwable {
gcDemo = this; //给gcDemo加了强引用
System.out.println("执行了finalize");
}
public static void main(String[] args) throws InterruptedException {
gcDemo = new GCDemo();
gcDemo = null; //去掉强引用
System.gc(); //垃圾回收
//睡眠一秒,以便于垃圾回收线程清理gcDemo对象。
Thread.sleep();
if(null != gcDemo) {
System.out.println("第一次gc后,alive");
} else {
System.out.println("第一次GC后,die");
}
gcDemo = null; //去掉强引用
System.gc(); //垃圾回收
//睡眠一秒,以便于垃圾回收线程清理gcDemo对象。
Thread.sleep();
if(null != gcDemo) {
System.out.println("第二次gc后,alive");
} else {
System.out.println("第二次GC后,die");
}
}
}
以上的代码很好的演示了对象在通过finalize()方法进行自救,以及第二次自救是否成功。
当然,输出结果为:
执行了finalize
第一次gc后,alive
第二次GC后,die
问题3:java虚拟机中存在哪几种引用?
1、强引用:如Object ob = new Object()。通过new出来的实例,就是强引用,如果该对象还在引用,就不会被回收。(可达性算法,引用计数算法都是基于强引用)
2、软引用:有用非必须的对象,JAVA中的SoftReference作为软引用对象。如果内存足够不会被回收,如果内存要溢出时候,就会被回收。如果回收后内存依然溢出,就会出现OutMemory的异常。
3、弱引用。说白了只能活一天的对象。JAVA中的WeekReference作为弱引用对象。在一次生命周期的结束时,无论内存是否足够,都被回收。
4、虚引用。最弱的引用关系,JAVA中的PhantomReferene作为虚引用对象。我不知道干嘛的。
问题4:方法区的垃圾如何回收?
方法区中要回收的主要是两类型:
1、废弃常量。
如何判断废弃常量呢?以字面量回收为例,如果一个字符串“abc”已经进入常量池,但是当前系统没有任何一个String对象引用了叫做“abc”的字面量,那么,如果发生垃圾回收并且有必要时,“abc”就会被系统移出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
2、无用的类。
如何判断无用的类,满足一下三个条件。
1、该类的所有实例被回收。堆中没有该类的任何实例。
2、加载该类的ClassLoader被回收。
3、该类对于的java.lang.Class对象没有在任何地方被引用,任何地方无法通过放射调用该方法。
问题5:常用的垃圾回收算法?
1、标记-清除算法(Mark-Sweep)
分为两个阶段:如图所示,当GCRoot没有引用到B时候,B被进行了标记,然后被清除。存活的对象不进行移动。
缺点:会产生内存碎片。会使得大对象无法创建。
2、复制算法(Copying)
他将内存分为两块(a1,a2),每次值使用其中一块(a1),当其中一块(a1)内存不足时候,就会将存活对象复制给另一块(a2),这样a1就变成了由原来的满内存变成空闲内存,a2由空闲内存变成有对象的内存。在下一次进行Copy的算法时候,就会在新的有对象的内存(a2)中进行回收。
3、标记整理算法(Mark-Compant)
过程和标记-清楚算法一样,唯一不同的是会进行对象的移动。解决了内存碎片的问题。
4、分代回收算法。
图可得:将对象的生命周期作为内存划分成若干个不同的区域。新生代(YoungGeneration),年老代(OldGeneration),永久代(PermanentGernation)
在新生代中:分为了Eden区(好像是夏娃住的地方,伊甸),survivor1区(From space),survivor2区(To space)(8:1:1的比例)。大部分对象在Eden区中生成,在进行Minor GC时候,将Eden区的存活对象转移至Survivor1区中,其他对象进行回收,当survivor1区中的内存满时候,将Eden区和Survivor1区中的存活对象传至survivor2区中,其他对象进行Minor GC。完成后将两个survivor区进行交换,以此往复。
当survivor2区中的内存不足以放下eden区和survivor1区中对象时候,会讲对象放置在年老代中,若年老代也也放不下了,就执行FullGC,将新生代,年老代的对象进行回收。
Full GC的效率低,因为对象多,但是频率低,常在年老代中进行,System.gc()会触发该GC。
Minor GC的效率高,频率高,常在新生代中进行(不一定等Eden区满才执行)。
年老代中:在年轻代中经历了N次的回收,仍然存活的对象,会被放入年老代中,因此可以认为年老代放的对象的生命周期都很长。而内存也比新生代大,约为新生代的2倍。
永久代中:永久代存放的是静态文件,如java类,方法等。回收的方法在问题4中。
5、垃圾回收器。
上图展示了7种不同的垃圾收集器。如果两个收集器之间有线连起来的话,就代表能搭配使用。
年轻代中的收集器包括:Serial收集器、ParNew收集器、ParallelScavenge收集器。老年代中的收集器包括:CMS收集器、MSC收集器,Parallel Old收集器。
1、Serial收集器:
一款年轻代中使用的单线程垃圾收集器。使用的是标记-复制算法。在进行GC时候,其他的用户线程会进行stop the world(STW),会线程的运行,程序会出现卡住的现象。
如果长时间、频繁的STW,会导致系统的反应迟钝。
2、ParNew收集器:
由图可得,这是Serial收集器的对线程版,同样是标记-复制算法。在很多时候作为年轻代的首选收集器,且和老年代中的CMS收集器搭配使用。
但如果运行在单核的机器中,他的性能不会比Serial线程好,相反会更差。 PareNew收集器默认开启的垃圾回收线程和当前机器的CPU数量一样,为了控制GC线程的 数量,我们可以通过-XX:+ParallelGCThreads来控制垃圾收集的线程。
3、ParallelScavenge收集器:
同样是一款年轻代的垃圾收集器,同样是多线程操作,同样是标记-复制算法。但是却和ParNew收集器有很大的不同,ParallelScavenge收集器更注重于缩短回收的时间,关注如何控制系统的
吞吐量(CPU用于运行应用程序和CPU总时间的对比),吞吐量=应用程序运行时间/(应用程序运行时间+GC时间)。
总结:年轻代中分为三种不同的收集器,Serial收集器,ParNew收集器, ParallelScavenge收集器,采用的算法都是标记-复制算法,但是性能会在不同数量的CPU下会有所不同。
4、Serial Old收集器:
由图可得,他是老年版本的Serial收集器,一款单线程收集器,但使用的是标记-整理算法。
5、Parallel Old收集器:
一款老年版本的Parallel Scavenge的收集器,使用标记-整理算法。工作原理和Parallel Scavenge相似,都是关注吞吐量。如果搭配这两款收集器,将可以实现JAVA对吞吐量优先的收集策略。
6、CMS收集器:
由图可得,这是一款在老年代中采用标记-清除算法的一款多线程收集器。
CMS收集器一共分成四个阶段:
1、初始标记,在线程到达safepoint时候,会进行第一阶段,因为这里是单线程的操作,因此会发生STW,但是速度很快,基本不会影响线程的运行。
2、并发标记,这个阶段是一个并发阶段,标记过程也比较耗时,但也不影响系统的运作。
3、重新标记,由图可知这是一款多线程的标记工作,但是同时也会STW,重新标记的耗时会比初始标记场,但是远小于并发标记。
4、并发清理,和并发标记一样,会相对耗时,但不影响系统运作。
CMS收集器实现了低延迟并发收集工作,但是也会有不足。
CMS默认开启的垃圾回收线程数量是(CPU+3)/4,随着CPU的增加,垃圾回收线程占用CPU的资源会减少,但是如果CPU少于4个的时候,垃圾回收的占比就好愈来愈大。比如目前CPU有2个,回收的线程占比将会大于50%。因此在在采取该收集器,应当考虑系统是否依赖CPU。
其次,在并发标记的过程中会产生浮动垃圾,由于在标记的过程中产生了垃圾,而CMS也无法进行标记,可能会导致老年代发送MajorGC(Full GC).。在JDK5中,当老年代到达68%的内存时候,就好激发MajorGC,而我们可以通过-XX:CMSInitiatingOccupancyFraction进行控制。
同时,CMS收集器因为算法的问题,在垃圾回收时会产生内存碎片,因此提供了-XX:+UseCMSCompactAtFullCollection参数在有必要时用于压缩处理,但因为该操作是单线程,所以会引起STW。虚拟机还提供了一个"-XX:CMSFullGCsBeforeCompaction"参数,来控制进行过多少次不压缩的Full GC以后,进行一次带压缩的Full GC,默认值是0,表示每次在进行Full GC前都进行碎片整理。
java虚拟机——垃圾回收机制的更多相关文章
- Java虚拟机垃圾回收机制
在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中.如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致 ...
- java虚拟机垃圾回收机制详解
首先,看一下java虚拟机运行的时候内存分配图: jvm虚拟机栈:一个是线程独有的,每次启动一个线程,就创建一个jvm虚拟机栈,线程退出的时候就销毁.这里面主要保存线程本地变量名和局部变量值. 本地方 ...
- 老生常谈Java虚拟机垃圾回收机制(必看篇)
二.垃圾收集 垃圾收集主要是针对堆和方法区进行. 程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收. 判断一 ...
- 记录Java的垃圾回收机制和几种引用
一.Java的垃圾回收机制 Java的垃圾回收机制(java garbage collection)是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的堆内存空间. ...
- Java虚拟机垃圾回收(三) 7种垃圾收集器
Java虚拟机垃圾回收(三) 7种垃圾收集器 主要特点 应用场景 设置参数 基本运行原理 在<Java虚拟机垃圾回收(一) 基础>中了解到如何判断对象是存活还是已经死亡?在<Java ...
- Java虚拟机垃圾回收:内存分配与回收策略 方法区垃圾回收 以及 JVM垃圾回收的调优方法
在<Java对象在Java虚拟机中的创建过程>了解到对象创建的内存分配,在<Java内存区域 JVM运行时数据区>中了解到各数据区有些什么特点.以及相关参数的调整,在<J ...
- Java虚拟机垃圾回收(三): 7种垃圾收集器(转载)
1.垃圾收集器概述 垃圾收集器是垃圾回收算法(标记-清除算法.复制算法.标记-整理算法.火车算法)的具体实现,不同商家.不同版本的JVM所提供的垃圾收集器可能会有很在差别,本文主要介绍HotSpot虚 ...
- Java虚拟机垃圾回收(二) :垃圾回收算法(转载)
1.标记-清除算法 标记-清除(Mark-Sweep)算法是一种基础的收集算法. 1.算法思路 "标记-清除"算法,分为两个阶段: (A).标记 首先标记出所有需要回收的对象: 标 ...
- 面试官,不要再问我“Java GC垃圾回收机制”了
Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...
随机推荐
- Mysql推荐使用规范
一.基础规范 使用InnoDB存储引擎支持事务.行级锁.并发性能更好.CPU及内存缓存页优化使得资源利用率更高 推荐使用utf8mb4字符集无需转码,无乱码风险, 支持emoji表情以及部分不常见汉字 ...
- C++_day9am
dynamic_cast static_cast reinterpret_cast #include <iostream> using namespace std; class A{ pu ...
- HTML5本地存储之本地数据库篇
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title&g ...
- POJ 1321 棋盘问题(搜索的方式)
Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...
- 【设计模式】工厂模式(Factory Pattern)
[前言] 很多时候我们编写了好几个接口的实现类,这些实现类分别有不同特性,用在不同的情景下.而我们对于这些实现类,也往往不会对外暴露内部增加的方法,只希望外部调用接口的方法,在这种情况下,我们没必要让 ...
- angular 引入编辑器以及控制器的学习和理解。。。
在angular中引入编辑器的时候花了很长时间,然后发现自己以前根本就没好好用过angular,因为项目是接手的学姐的,学姐又是接手的学姐的,到我这里就只是写写页面的事了. 引入编辑器差了好多好多资料 ...
- switch与if语句的应用
C语言自学之switch与if语句的应用 #include<stdio.h> #include<stdlib.h> int main() { ;//需要计算的年份 ;//需要计 ...
- package.json和npm install、cnpm install 的問題
問題:最近使用cnpm安装项目依赖后,运行项目出现样式错乱问题. 描述:最近项目开发,需求参插了很多个版本,所以在前端项目的主干上拉好几套分支代码.拉的分支并不会把node_modules也拉过去,所 ...
- .NET Core和Swagger 生成 Api 文档转
阅读目录 1.引用 2.打开startup.cs文件 3.设置XML注释 4.运行结果 5.主要问题的解决办法 6.可以自定义UI 前言 最近写了好多Web api, 老大说太乱了,要整理一下,使用S ...
- Webpack学习-工作原理(下)
继上篇文章介绍了Webpack的基本概念,完整流程,以及打包过程中广播的一些事件的作用,这篇文章主要讲生成的chunk文件如何输出成具体的文件.分同步和异步两种情况来分析输出的文件使用的webpack ...