JVM类加载过程和GC机制
Posted 3 ERROR(s)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM类加载过程和GC机制相关的知识,希望对你有一定的参考价值。
文章目录
一、JVM运行时数据区
1.堆区(线程共享)
如果是基本数据类型,看他是局部变量还是成员变量,局部变量在栈上开辟空间,成员变量在堆区开辟空间。
如果是引用数据类型,JVM在堆中创建对象,对象的引用存在虚拟机栈上的局部变量表中。
4.方法区(元数据区)(线程共享)
里面放的都是类对象
- 包含了这个类的各种属性的名字,类型,访问权限
- 包含了这个类的各种方法的名字,参数类型,访问权限,以及二进制代码
- 包含这个类的static成员
对于 HotSpot 来说,JDK 8方法区的内存属于本地内存,这样方法区空间的大小就不在受 JVM 最大内存的参数影响了,而是与本地内存的大小有关。
JDK 8 中将字符串常量池移动到了堆中。
2.Java虚拟机栈(线程私有)
Java 虚拟机栈的作用:Java 虚拟机栈的生命周期和线程相同,Java 虚拟机栈是用来描述 Java 方法执行的
内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数
栈、动态链接、方法出口等信息。咱们常说的堆内存、栈内存中,栈内存指的就是虚拟机栈。
3.本地方法栈(线程私有)
本地方法(native method)指的是JVM内部的方法(C++写的)
5.程序计数器(线程私有)
.java代码 =》.class(二进制字节码,里面就是一些指令) =》放到内存中 =》每条指令都有自己的地址=》 CPU执行指令就要从内存中取除地址,然后再在CPU上执行
二、类加载过程
类加载就是从加载到初始化这个过程
1.加载:通过全限定名称找到这个类的二进制字节流,在内存中生成一个代表这个类的Java.long.Class对象作为这个类的各种访问数据的入口
2.链接
- ( 1 )验证 验证这个.Class文件包含的信息符合《Java虚拟机规范》
- ( 2 )准备 给类对象成员分配空间并且初始化为0。
- ( 3 )解析 初始化字符串常量
3.初始化 初始化static变量,执行static代码块
双亲委派机制
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,因此所有的加载请求最 终都应该传送到最顶层的启动类加载器中,如果父类中找不到这个类,就交给子类加载器完成。
- 启动类加载器
- 应用程序类加载器
- 自定义类加载器
双亲委派机制优点
1.避免重复加载类,这样一套机制的目的就是创造一个优先级顺序
2.保证Java核心API的安全性,因为有些类是用户自己写的,不能保证他的安全性
三、垃圾回收
1.回收的是哪些内存
- 堆
- 方法区
其他的内存区都是随着线程的销毁能自动释放了。
我们此处讨论的垃圾回收 具体是指堆内存的回收。
2.死亡对象判断的算法
(1).引用计数(Java根本不用这个)
给对象增加一个引用计数器,每当有一个地方引用他的时候,计数器自增;当引用失效的时候计数器自减,当计数器为0的时候我们就认为这个对象不能再被使用了,判断他“死亡”。
优点:规则简单,实现方便,判定高效。
缺点:如果对象很多,但是引用少的情况加一个int型的计数器会导致得不偿失,内存压力大。他就适合对象少,引用多的情况。
还有一个致命的缺点,循环引用可能导致计数器失效。
(2).可达性分析
通过一系列我们称之为GCRoost 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象和引用链没有任何相连的时候,我们就可以说这个对象是不可用的。
GCRoots对象可以包含以下几种:
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中Native引用的对象
- 虚拟机栈中引用的对象
四种引用情况:
- 强引用:只要强引用存在,他就不可能回收被引用的对象。
- 软引用:有用但不必须,如果要发生内存溢出了,JVM就会把他们回收了,如果经过这次回收还是空间不足才会抛出溢出异常。
- 弱引用:被弱引用关联的对象只能生存到下一次垃圾回收发生前,下一次人家开始回收,不管内存溢出不溢出,它一定被回收。
- 虚引用:不能通过虚引用获得一个对象实例,他存在的意义就是这个对象被回收的时候能收到一个系统通知。
3.垃圾回收策略
(1) 标记-清除算法
首先标记所有要回收的对象,然后再进行统一回收。
缺点:
- 效率非常低,不管是标记还是清除,
- 内存碎片化问题, 回收之后的内存空间不连续,如果有较大的对象要存入的话就要提前进行二次回收了。
(2) 复制算法
它将可用内存按照大小划分为相等的两块,每次只用其中的一块,在标记完清除的对象后,就把这片区域还存活的对象复制到另外一边内存上去,然后回收的时候把当前内存整体回收。
优点:解决了内存碎片化的问题
缺点:每次只有一半内存可用,当回收的对象少的时候这种办法显然不合适,他适合对象被快速回收,并且对象不多需要的内存不大。
(3)标记-整理
复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法。
为了解决这个问题,我们对需要清除的对象先标记,再清除,然后让所有存活的对象向一端移动。
(4)分代算法
- 新生代:一般对象创建后都会进入新生代
- 老年代:该对象经过(15)次内存回收之后会变为老年代
- 当Eden区满的时候,会触发第一次Minor gc,把还活着的对象拷贝到Survivor From区;当
Eden区再次触发Minor gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过
这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。 - 当后续Eden又发生Minor gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到
From区域,并将Eden和To区域清空。 - 部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数
MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代
4.如果某个对象非常大,占的内存大于生存区的大小,就将它直接放入老年代。老年代如果满了就会发生Full GC
以上是关于JVM类加载过程和GC机制的主要内容,如果未能解决你的问题,请参考以下文章
36.JVM内存分哪几个区,每个区的作用是什么如和判断一个对象是否存活java垃圾回收机制垃圾收集的方法有哪些java类加载过程类加载机制双亲委派Minor GC和Major GC