JVM体系结构概述
谈谈你对JVM的理解?java8的虚拟机有什么更新?
什么式OOM?什么式StackOverflowError?有那些方法分析?
JVM的常用参数调优你知道哪些?
内存快照抓取MAT分析DUMP文件知道吗?
谈谈JVM中,对类加载器你的认识?
JVM位置
JVM体系结构图
垃圾回收在橙色部分,生命周期比较长
类装载器就像是jvm的入口
类装载器 ClassLoader
负责加载class文件,class文件在文件开头有特定的文件表示,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Executin Engine决定
jvm的入口
类加载器的种类
双亲委派机制
保证JVM不被加载的代码破坏,防止类的重复加载,保证Java核心包的类不被替换
启动类加载器是C++写的,java打印结果为null
public class Demo1 {
public static void main(String[] args) {
Object obj = new Object();
Demo1 d01 = new Demo1();
System.out.println(d01.getClass().getClassLoader().getParent().getParent());
System.out.println(d01.getClass().getClassLoader().getParent());
System.out.println(d01.getClass().getClassLoader());
/**
* null
* sun.misc.Launcher$ExtClassLoader@1540e19d
* sun.misc.Launcher$AppClassLoader@18b4aac2
*/
}
}
沙箱安全机制
执行引擎 Execution Engine
Execution Engine 执行引擎负责解释命令,提交给操作系统执行。jvm的出口
本地方法接口 Native Interface
java里标native修饰的方法都没有方法体。比如Thread里的
private native void start0();交给操作系处理
new Thread().start(); //这行代码不能确定线程是否启动,因为是操作系统启动的
本地方法栈 Native Method Stack
本地方法栈是为虚拟机使用到的 Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。
本地方法库
java调用Redis,MySQL等jar包统一需要本地方法库
windows系统玩单机游戏缺少dll,动态连接库
java执行不了的方法需要求助操作系统,把native标注的方法塞进本地方法栈,出栈时调用操作系统的本地方法接口,调接口时如果需要第三方的支持,使用本地方法库
![]()
方法区 Method Area
方法区是所有线程共享,所有字段和方法字节码,以及一些特殊方法和构造方法,接口代码也在此处定义。简单说,所有定义的方法都保存在此区域,此区域属于共享区间。
class模版放在方法区中,Class类的东西都要放进去
静态变量 + 常量 + 类信息(构造方法/接口定义)+ 运行时常量池 存在方法区中
但是,实例变量存在堆内存中,和方法区无关
什么时候回收?jvm停下来的时候回收,生命周期很长。
java栈 java stack
与程序计数器是搭档
栈也叫栈内存,主管java程序的运行,是在线程创建的时候创建,它的生命周期是跟随线程的生命周期,线程结束的时候内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的
8种基本数据类型 + 对象的引用变量 +实例方法 都是在函数的栈内存中分配
栈存储什么?
栈帧主要保存三类数据:
本地变量 Local Variables 输入参数和输出参数以及方法內的变量
栈操作 Operand Stack 记录出栈,入栈的操作
栈帧数据 Frame Data 包括类文件,方法等
每执行一个方法都会产生一个栈帧,保存到栈顶部,顶部栈就是当前的方法,该方法执行完毕后就会自动将此栈帧出栈,main方法在栈底部
public class Demo1 {
public static void main(String[] args) {
test01();
System.out.println("main end");
}
public static void test01(){
test02();
System.out.println("test01 end");
}
public static void test02(){
System.out.println("test02 end");
}
}
/*
test02 end
test01 end
main end
*/
StackOverflowError
Exception in thread "main" java.lang.StackOverflowError
循环递归
栈 + 堆 + 方法区的交互关系
HopSpot是使用指针的方式来访问对象:
Java堆中会存放访问类元数据的地址,reference存储的就是直接对象的地址
程序计数器 PC寄存器
每个线程都有一个程序计数器,是线程私有的(生命周期短,不需要垃圾回收),就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
堆
要注意,这个“堆”并不是数据结构意义上的堆(Heap (data structure),一种有序的树),而是动态内存分配意义上的堆——用于管理动态生命周期的内存区域。
JVM的堆被同一个JVM实例中的所有Java线程共享。它通常由某种自动内存管理机制所管理,这种机制通常叫做“垃圾回收”(garbage collection,GC)。JVM规范并不强制要求JVM实现采用哪种GC算法。
堆的数据结构更加复杂,jvm调优调的是堆
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类,方法,常/变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:
-
Young Generation Space 新生区 Young/New
-
Tenure Generation Space 养老区 Old/Tenure
-
Permanent Space 永久区 Perm
Java 7之前
新生区(幸存1区-> 养老区)经历GC(垃圾回收)之后,才能进入下面的区域
注意:养老区到永久存储区没有通路
OutOfMemoryError
java.lang.OutOfMemoryError:java heap space异常
说明java虚拟机的堆内存不够
java.lang.OutOfMemoryError:PerGen space
程序启动大量需要加载的jar包。成为历史了
这两个异常也恰好说明新生区,养老区 和 元空间在内存不同的区域
方法区是一个接口,永久区是方法区的一个实现
新生区:由Eden,两块大小相同的Survivor(又称为from/to,s0/s1)构成,to总为空
旧生代:存放新生代中经历多次GC仍然存活的对象
比如:连接池对象
maven 配好就可以下载好相应的jar包
方法区接口有两个实现,1:永久区,2: 元空间
总结
在java7及之前,逻辑上分为三部分:新生区,养老区,永久区。新生区又可以分为伊甸区,幸存0区,幸存1区
在java8之后,逻辑上还是分为三部分:新生区,养老区,元空间
在物理上,分为两部分:新生区,养老区
Java虚拟机的堆、栈、方法区如何去理解?
JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
- 栈区:
- 每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不是对象),对象都存放在堆区中
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
- 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
- 堆区:
- 存储的全部是对象实例,每个对象都包含一个与之对应的class的信息(class信息存放在方法区)。
- jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。
- 方法区:
- 又叫静态区,跟堆一样,被所有的线程共享。它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
作者:EnjoyMoving
链接:https://www.zhihu.com/question/29833675/answer/207261960
来源:知乎
堆参数调优
jvm运行起来占用内存多少?
-Xms 本机内存的64分之一
-Xmx 本机内存的4分之一