1.JVM五大部分
(1)JVM五大部分作用简述
-
类加载器(Class Loader): 加载字节码文件到内存(次要)
-
运行时数据区(Runtime Data Area): JVM 核心内存空间结构模型(核心),其中方法区域和堆线程共享
-
执行引擎(Execution Engine): 对 JVM 指令进行解析,翻译成机器码,解析完成后提交到操作系统中。
-
本地库接口(Native Interface): 供 Java 调用的融合了不同开发语言的原生库。
-
本地方法库(Native Libraries): Java Native方法的具体实现。
2.运行时数据区五大部分
(1)方法区(共享)
-
方法区存储虚拟机加载的类信息、常量/常量池(如String s="123")、静态/类变量(static),即时编译器编译后的代码等数据。
-
方法区是一种规范,永久代是方法区的一种实现。
(2)本地方法栈
-
和虚拟机栈的作用和原理基本相同,都可以用来执行方法。不同点在于执行的是本地方法。
-
本地方法的使用原理理解(如C语言):1. 在 Java 程序中声明 native 修饰的方法,只有方法定义,没有方法实现,将该 Java 文件编译成字节码文件。2. 用 javah 编译字节码文件,生成一个 .h 文件。3. 写一个 .cpp 文件实现 .h 文件中的方法。4. 将 .cpp 文件编译成动态链接库文件 .dll 。5. 使用 System.loadLibrary() 加载动态连接库文件。
(3)程序计数器
-
占用内存空间较小,是当前线程所执行的字节码行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令。
-
多个线程之间的程序计数器相互独立,互不影响,这保证了每个线程都恢复后都可以找到具体的执行位置。
(4)堆(共享)
-
存放实例化对象,占用大部分的空间,是 GC 的主要管理区域,又分为年轻代、老年代、永久代(JDK8以后去掉)。
-
年轻代: 又可分为 Eden(伊甸),Survivor from,Survivor to1. Eden区:对象刚被创建的时候,存放在 Eden 区,如果 Eden 区放不下(触发MinorGC,复制算法),则放在 Survivor 区。2. Survivor区: GC 回收时,将 Eden 存活的对象存入 Survior From 中。 下一次回收时,将 Survior From 中的对象存入 Survior To 中,清除 Survior From。然后重复步骤, Survior From 变成 Survior To,Survivor To 变成 Survivor From。每次回收时,对象年龄+1,增长到一定程度的对象进入老年代。(注:Survior区满了是不能用复制算法)
-
老年代: 存放生命周期较长的对象(JDK 8 改用元空间替代永久代)老年代的空间也会不够用,最终会执行Major GC(MajorGC 的速度比 Minor GC 慢很多很多,据说10倍左右)。Major GC使用的算法是:标记清除(回收)算法或者标记压缩算法。
(5)虚拟机栈(Java执行的内存模型)
-
Java Method执行的内存模型
-
Java 栈中存放的是多个栈帧,每个栈帧对应一个被调用的方法,主要包括局部变量表(局部变量)、操作数栈、动态链接、方法返回地址(方法出口)。每一个方法的执行,JVM 都会创建一个栈帧,并且将栈帧压入 Java 栈,方法执行完毕,该栈帧出栈。


【本地语言内存空间区别】
-
代码区≠方法区,其他都一样(原始语言如C的内存空间分布如下)
3.内存中GC触发条件
(1)MinorGC(堆Eden->survivor):From和To互换实现原理
新生代Minor GC 复制算法: 把内存区域分为两块,每次使用一块,GC的时候把一块中的内容移动到另一块中,原始内存中的对象就可以被回收了。
-
优点是避免内存碎片。
-
缺点是内存满了不能使用
(2)MajorGC(堆survivor->older):标记清除或标记压缩
-
标记清除:
-
1. 首先会从GC root进行遍历,把可达对象(存过的对象)打标记
-
2. 再从GC root二次遍历,将没有被打上标记的对象清除掉。
-
优点:老年代对象一般是比较稳定的,相比复制算法,不需要复制大量对象。之所以将所有对象扫描2次,看似比较消耗时间,其实不然,是节省了时间。举个栗子,数组 1,2,3,4,5,6。删除2,3,4,如果每次删除一个数字,那么5,6要移动3次,如果删除1次,那么5,6只需移动1次。
-
缺点:这种方式需要中断其他线程(STW),相比复制算法,可能产生内存碎片。
-