1 jvm结构图
2 类的装载器
将类的字节码文件加载到jvm中,会把这些内容转化为方法区运行的数据结构。jvm会创建对应的Class对象,保存到堆中。
装载器有如下几种:虚拟机自带装载器:启动类加载器(BootStrap)C++、扩展类加载器(Extension)Java、应用程序类加载器(AppClassLoader)Java,也叫系统类加载器,加载当前应用的classpath的所有类
用户自定义加载器:Java.lang.ClassLoader的子类
双亲委派机制:如果一个类收到加载的请求,他首先会让它的父类尝试去完成,当他的父类无法完成时,它才会尝试加载这个类。
沙箱:比如启动类加载器它加载了java.lang.String,如果本地也是创建相同的包且有String类,它会以启动的时候首先加载的那个类为准。
3 执行引擎
功能:解释命令,提交给操作系统执行。
4 本地方法栈、本地方法接口、本地方法库
native修饰的方法是本地方法,由于java代码无能为力,可能需要调用操作系统或C++等代码。这些方法会注册到本地方法栈,这些本地方法通过调用本地方法接口调用非java代码,执行引擎会加载本地方法库。
5 PC寄存器
用于指向当前线程所执行的字节码的行号显示器
6 方法区
存储类的结构信息,有静态变量(静态域),运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容。在jdk7中这里是永久代,jdk8采用的是元空间实现。
7 栈
在线程创建时创建,线程结束栈内存也结束,所有不存在垃圾回收。栈中存储8种基本数据变量、对象的引用变量、实例方法。栈中存储的方法就是栈帧,存储三种数据,本地变量:输入输出参数以及方法内的变量,栈的操作:记录入栈和出栈、栈帧数据:方法。
栈中存储直接对象的地址,堆中存储对象以及类元数据的地址即Class的地址,方法区存储类元数据。
8 堆
在jdk7中堆在逻辑上分为新生区,养老区,永久存储区,而在物理上堆只有新生区和养老区,永久存储区为方法区,在jdk8中永久区被原空间取代,二者区别时永久存储区使用堆内存,而元空间的大小和物理内存有关。
在伊甸区如果对象满了,会触发 Minor GC ,会把存活的对象移动到幸存0区(from),对象的年龄加一。第二次伊甸区对象满了,会把幸存0区和伊甸区存活的对象复制到幸存1区并且年龄加一,幸存0区有from区变为to区,幸存1区由to变为from区,清理伊甸区和to区,这样一直进行下去,如果哪些对象年龄达到15,就会进入养老区,这个值也可以自己设置,在jdk8种在0-15之间。如果养老区满了,会触发full gc。
9 GC算法
分代收集算法,新生区和养老区采用的算法是不一样的,由于新生区发生gc比较频繁,而养老区次数比较少,在jdk7中永久存储区基本不清理,在jdk8中元空间不使用堆内存故不需要清理。
9.1 引用计数法
在每次引用时,会加一,置为null会减一,但是这个算法有如下缺点,要使用计数器,会有内存消耗,无法处理循环引用。
9.2 复制算法
触发 Minor GC ,会把存活的对象移动到幸存0区(from),对象的年龄加一。第二次伊甸区对象满了,会把幸存0区和伊甸区存活的对象复制到幸存1区并且年龄加一,幸存0区有from区变为to区,幸存1区由to变为from区,清理伊甸区和to区,这样一直进行下去,如果哪些对象年龄达到15,就会进入养老区。
缺点:①:消耗内存
②:如果生存率高的化,也是比较浪费的。
这个算法在新生区使用,因为新生区存活率低。
9.3 标记清除
①:从根集合开始扫描,对存活的对象进行标记
②:扫描整个内存空间,清理未被标记的对象。
缺点:需要暂停整个应用,会产生内存碎片
老年代一般是由标记清除或者是标记清除与标记整理的混合实现
9.4 标记压缩
①:从根集合开始扫描,对存活的对象进行标记
②:将标记的对象移动到一边,不需要清理
缺点:虽然不需要回收对象,但是不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
还有一个算法是标记清除压缩算法,这个算法结合3,4,和标记清除是类似的,只不过多次gc后才进行压缩。
老年代一般是由标记清除或者是标记清除与标记整理的混合实现