一、类加载机制
什么是类加载
类加载就是将 .class 文件的二进制数据读取到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装在方法区内数据的数据结构。类加载的最终产品是位于堆区中的 Class对象,它向程序员提供了访问方法区内数据结构的接口。
类的生命周期
注意:各个周期只是以这个顺序开始而已,过程和结果很可能不会按照此顺序进行!
加载-->验证-->准备-->解析-->初始化-->使用-->卸载
加载:查找并加载类的二进制数据,在堆区中创建一个 java.lang.Class对象
验证:验证文件格式、元数据、字节码、符号引用
准备:给类的静态变量分配内存,并初始化默认值(0或者" ")
初始化:给类的静态变量正确的初始值
使用:new 对象实例使用
卸载:对象无引用时,执行垃圾回收
类加载器
下面是几种类加载器及各自负责加载类库的描述:
-
启动类加载器:Bootstrap ClassLoader
-
jdk/jre/lib 目录下的类库
-
被 -Xbpptclasspath 参数指定的路径中的类库
-
并且能被虚拟机识别的类库
-
-
扩展类加载器:Extension ClassLoader
-
jdk/jre/lib/ext 目录下的类库
-
由 java.ext.dirs系统变量指定的路径中的所有类库(如:java.* 开头的类)
-
-
应用程序类加载器:Application ClassLoader
- 加载用户类路径(ClassPath)指定的类
双亲委派模型
-
全盘负责
当一个类加载器要加载某个类时,那么这个类所依赖和引用的其他类也由这个类加载器负责一块载入。
除非规定显示使用另一个类加载器载入
-
父类委托
先让父类加载器试图加载该类,只有父类加载器无法加载该类时才尝试才从自己的路径中加载该类
-
缓存机制
保证所有加载过的类都被缓存,在程序中需要使用某个类时,类加载器先去缓存区寻找该类,
如果缓存区内不存在,系统才会去加载该类,并转换成 java.lang.Class 对象存入缓存区,这就是为什么修改了 java文件后,需要重启jvm,程序的修改才会生效
所以什么是双亲委派模型?
这里举个例子:
现在有一个类加载器 A,某时刻A收到了一个类加载的请求,这时它不会立刻去加载,
而是先将这个请求委托给 它的父类加载器,如果父类加载器还有父类那就再委托,直到委托到最顶层的“祖宗加载器”为止。
由这个“祖宗”去处理这个类加载的请求,如果连“祖宗”加载器都不能加载,这时 A才会自己去尝试加载这就是双亲委派模型
二、JVM内存结构
都有哪些内存结构
线程共享:java堆、方法区
线程私有:java栈、本地方法栈、程序计数器
-
java堆(共享)
java虚拟机所管理的内存中最大的一块
存在的唯一目的:存放对象实例,几乎所有的对象实例在这里都有分配内存
-
方法区(共享)
存在的目的:存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
-
java栈(私有)
每个java方法被执行的时候都会创建一个栈帧(Stack Frame),用于存储局部变量、操作栈、动态链接、方法出口等信息。
每个方法从被调用至执行完成的过程,就对应着一个栈帧在虚拟机从入栈到出栈的过程 -
本地方法栈(私有)
作用于 java栈类似,区别不过是:【java栈为虚拟机执行java方法服务】,【本地方法栈则是为虚拟机使用到的 Navtive方法服务】
-
程序计数器(私有)
存在的目的:当前线程执行的字节码的行号指示器
jvm对象分配规则
-
对象优先分配在伊甸区(Eden),如果伊甸区没有足够的空间时,虚拟机执行一次 Minor GC
-
大对象(大对象:需要大量连续内存空间的对象。这里有个疑问:大量是多少?有没有标准?)直接进入老年代,这样做的目的是:避免在 伊甸区(Eden)和幸存者区(Survivor)之间发生大量的内存拷贝
-
虚拟机为每个对象定义了一个年龄计数器,如果对象经过了一次 Minor GC,那么对象会进入幸存者区,之后每经过一次 Minor GC,对象的年龄会+1。直到特定的年龄进入老年区
-
动态判断对象的年龄,如果幸存者区中,相同年龄(假设为x)的所有对象大小总和大于幸存者区的一半,那么所有年龄>=x的对象都将会直接进入老年代
-
空间分配担保,每次 Minor GC,jvm会计算从幸存者区移至老年区对象的平均大小,如果平均值大于老年区的内存空间剩余值大小,则进行一次 Full GC,如果小于则检查 HandlePromotionFailure 设置,并判断若设置为true,则进行 Minor GC,否则进行 Full GC
三、垃圾回收
对象存活判断
判断对象是否存活一般有两种方式:
-
引用计数
每个对象都有一个计数属性,新增一个引用时,计数+1;释放一个引用时,计数-1。
计数=0 时就表明这个对象已经死了,此法不能解决对象互相循环引用问题
-
可达性分析
从 GC Roots 开始向下搜索,搜索过的路径称为引用链,
当一个对象没有任何引用链相连,则证明此对象已经死了,是不可达对象
GC算法
【待补充ing……】
【以上内容是个人的学习总结,不慎之处欢迎评论斧正】