初探JVM
1. JVM的体系结构
- 其中栈区由于函数返回后就会出栈,因此不会存在垃圾回收。垃圾回收主要针对的是堆区的回收。
- JNI:Java Native Interface,通过使用Java本地接口书写程序,可以确保代码在不同的平台上方便移植。
2. 类加载器
类的生命周期
类的加载过程详细参考Class文件是如何被加载进JVM
类加载器的分类
BootstrapClassLoader(启动类加载器)
c++
编写,加载java
核心库 java.*
,构造ExtClassLoader
和AppClassLoader
。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
ExtClassLoader (标准扩展类加载器)
java
编写,加载扩展库,如classpath
中的jre
,javax.*
或者
java.ext.dir
指定位置中的类,开发者可以直接使用标准扩展类加载器。
AppClassLoader(系统类加载器)
java编写,加载程序所在的目录,如
user.dir所在的位置的
class
CustomClassLoader(用户自定义类加载器)
java
编写,用户自定义的类加载器,可加载指定路径的class
文件
双亲委派机制
特点
- 一个类加载器收到了类加载请求,不会自己立刻尝试加载类,而是把请求委托给父加载器去完成,每一层都是如此,所有的加载请求最终都传递到最顶层的类加载器进行处理;
- 如果父加载器不存在了,那么尝试判断有没有被启动类加载器加载;
- 如果的确没有被加载,则再自己尝试加载。
流程
好处
- 双亲委派机制使得类加载出现层级,父类加载器加载过的类,子类加载器不会重复加载,可以防止类重复加载;
- 使得类的加载出现优先级,防止了核心API被篡改,提升了安全,所以越基础的类就会越上层进行加载,反而一般自己的写的类,就会在应用程序加载器(Application)直接加载。
3. 数据区是如何工作的
4. 堆
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。一般堆中存放的是类,方法,常量,变量,以及所有引用类型的真实对象。
堆区逻辑结构
堆区的逻辑结构分为两部分,非堆区和堆区,非堆区即方法区,JDK8之前叫做永久带,JDK8之后叫做元空间。堆区主要分为老年带和新生带。结构图如下:
如上图所示,堆区的主要结构可细分为:
- 新生区
- Eden区
- 幸存from区
- 新区to区
- 老年区
新生区
Young GC
新生区是类诞生和成长的地方,甚至会在这里死亡。当一个对象被new出来时,会首先进入Eden区,Eden区慢慢积累满了之后,触发第一次Young GC,存活对象拷贝到Survivor的from区,清空Eden区。当第二次Eden区满时,再次触发Young GC,扫描Eden区和from区,把存活的对象复制到To区,清空Eden区和from区。如果此时Survivor区的空间不够了,就会提前把对象放入老年代。
Full GC
当新生带都满了时,即Young GC已经清理不出空间时,就会触发Full GC,这次重GC会将整个堆中的对象做一次GC。如果还是没有空闲的空间腾出,就会抛出异常:java.lang.OutOfMemoryError: Java heap space
老年区
由新生区过渡而来,默认的,新生区中一个对象在from区和to区来回交换15次后,如果对象最终还是存活,就放入老年代。
永久带/元空间
这个区域是常驻内存的。用来存放JDK自身携带的Class对象,Interface元数据,存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!当关闭JVM虚拟机就会释放这个区域的内存。
元空间:逻辑上存在,物理上不存在
5. GC垃圾回收
判断对象可用算法
引用计数法
给对象一个引用计数器refCount。每有一个对象引用它,计数器加1,当refCount=0的时候,表示对象不再可用。
缺点:对象循环引用时,即使两个对象都不再被访问,计数器也不为0.
可达性分析算法
如上图,从GC Roots开始向下搜索,连接的路径为引用链;GC Roots不可达的对象被判为不可用。
垃圾回收算法
复制算法
- 将可用内存分为容量大小相等的两块,每次只使用其中一块;
- 当一块用完,就将存活着的对象复制到另一块,然后将这块全部内存清理掉;
一般新生代(伊甸园区、幸存区)会使用复制算法,生成新的to区
优点:没有内存碎片。
缺点:内存的利用率变低,可用内存缩小为原来的一半;如果存活数量比较大,复制性能会变低。
标记清除
缺点:两次扫描,严重浪费时间,会产生内存碎片。
优点:不需要额外的空间。
标记压缩
对于标记清除的再压缩
缺点:整理阶段存在效率问题,适合老年代这种垃圾回收频率不是很高的场景。
优点:不会产生内存碎片;不需要浪费额外的空间进行分配担保。
分带收集算法
当前商业虚拟机都采用该算法。
新生代
:复制算法(GC后只有少量的对象存活)老年代
:标记-清除-压缩算法 (GC后对象存活率高)