虚拟机栈:
Stack Frame 栈帧(每个方法执行的时候都会形成一个栈帧,存储局部变量表(存放的 8 个基本数据类型(把值直接放在局部变量表里)、对象的引用类型(1.通过句柄的方式;2. 直接指向的方式)来操作真正想要使用的对象)、操作数栈、动态链接、方法出口相关信息);虚拟机栈归属于一个特定的线程,是线程独有的一块内存空间
一个方法的执行过程,就是这个方法对于栈帧的入栈、出栈过程。
程序计数器(Program Counter):
当前执行的字节码的行号指示器;线程私有
本地方法栈:
与虚拟机栈类似,结构也类似,主要用于执行 native 方法
堆(Heap):
java 虚拟机管理的最大的一块内存,由所有线程共享,虚拟机启动时就会创建,唯一目的就是存放对象实例。java 是通过引用(引用本身是一个变量,位于局部变量表)操作对象,对象位于 Heap 上,而引用位于 Stack 上,
与 堆 相关的一个重要概念是 GC,是 GC 的主要工作区域。现代几乎所有的 GC 都是采用分代收集算法,所以,堆空间也基于这一点进行了相应的划分:新生代和老年代。再细致,Eden 空间, From Survivor 空间和 To Survivor 空间
方法区(Method Area):
存储元信息,如存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。 永久代(Permanent Generation),从jdk 1.8 开始,已经彻底废弃了永久代,使用 元空间(meta space) 存放每个 Class 的结构信息,包括常量池、字段描述、方法描述;GC 的非主要工作区域。
运行时常量池
本身是 方法区 的一部分。存放编译期生成的各种字面量和符号引用
直接内存(Direct Memory):
并不是 jvm 关系的区域,由操作系统来管理。 与 java NIO 密切相关,jvm 是通过堆上的 DirectByteBuffer 来操作直接内存
关于 java 对象的创建过程
new 关键字创建对象的 3 个步骤:
- 在堆内存中创建对象的实例
- 为对象的实例成员变量赋值(静态变量在 初始化 阶段完成了)(调用<init>方法)
- 将对象的引用返回
指针碰撞
前提是堆中的空间通过一个指针进行分割,一侧是已经被占用的空间,另一侧是未被占用的空间。
空闲列表:
前提是堆内存空间中已被使用与未被使用的空间是交织在一起的,这时,虚拟机就需要通过一个列表来记录哪些空间是可以使用的,哪些空间是已被使用的,接下来找出可以容纳下新创建对象且未被使用的空间,在此空间存放该对象,同时还要修改列表上的对象。
对象在内存的布局:
- 对象头(对象要运行时的一些信息,如 hashcode)
- 实例数据(即我们在一个类中所声明的各项信息)
- 对齐填充(可选) !
引用访问对象的方式:
- 使用句柄的方式。
- 使用直接指针的方式。
public static void main(String[] args) {
//-Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError 设置jvm对空间最小和最大以及遇到错误时把堆存储文件打印出来
//打开jvisualvm装在磁盘上的转存文件
List<MyTest1> list = new ArrayList<>();
while (true) {
list.add(new MyTest1());
System.gc();
}
}
虚拟机栈溢出
测试调整虚拟机栈内存大小为: -Xss160k,此处除了可以使用JVisuale监控程序运行状况外还可以使用jconsole
public class MyTest2 {
private int length;
public int getLength() {return length;}
public void test () {
this.length++;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
test();
}
public static void main(String[] args) {
MyTest2 myTest2 = new MyTest2();
try {
myTest2.test();
} catch (Throwable e) {
System.out.println(myTest2.getLength());
e.printStackTrace();
}
}
}
死锁
public class MyTest3 {
public static void main(String[] args) {
new Thread(() -> A.method(), "Thread-A").start();
new Thread(() -> B.method(), "Thread-B").start();
}
}
class A {
// 当一个线程进入静态方法,持有的不是这个类的对象的锁,而是这个类对应的 Class 的锁
public static synchronized void method() {
System.out.println("method from A");
try {
Thread.sleep(5000);
B.method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class B {
public static synchronized void method() {
System.out.println("method from B");
try {
Thread.sleep(5000);
A.method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
方法区产生内存溢出错误
如果不采取一些措施,方法区产生内存溢出错误是极难的。jdk 1.8 开始,方法区废除了永久代,采用了元空间,元空间采用的是操作系统本地的内存,初始内存是 21 M,并且随着对内存空间的不断占用,元空间虚拟机会进行垃圾回收,如果回收不够的话,还会进行内存的扩展,一直扩展到物理内存的最大限度。
显示设定元空间的大小
设置元空间大小:-XX:MaxMetaspaceSize=100m 关于元空间参考:www.infoq.cn/article/jav…
jmap -clstats PID 打印类加载器数据。(-clstats 是 -permstat 的替代方案,在 JDK8 之前,-permstat 用来打印类加载器的数据)。下面的例子输出就是 DaCapo’s Avrora benchmark 程序的类加载器数据
jstat -gc LVMID 用来打印元空间的信息,具体内容如下

jvm 命令使用
/**
* Created BY poplar ON 2019/11/26
* jmam命令的使用 -clstats<pid>进程id to print class loader statistics
* jmap -clstats 3740
*
* jstat -gc 3740
* S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
* 512.0 512.0 0.0 0.0 24064.0 9626.0 86016.0 1004.1 4864.0 3758.2 512.0 409.1 144 0.064 0 0.000 0.064
* MC元空间总大小,MU元空间已使用的大小
*/
public class MemoryTest4 {
public static void main(String[] args) {
while (true)
System.out.println("hello world");
}
//查看java进程id jps -l
// 使用jcmd查看当前进程的可用参数:jcmd 10368 help
//查看jvm的启动参数 jcmd 10368 VM.flags
// 10368:-XX:CICompilerCount=3 -XX:InitialHeapSize=132120576 -XX:MaxHeapSize=2111832064 -XX:MaxNewSize=703594496
// -XX:MinHeapDeltaBytes=524288 -XX:NewSize=44040192 -XX:OldSize=88080384 -XX:+UseCompressedClassPointers
// -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
}
jvm 常用命令
jcmd (从JDK 1. 7开始增加的命令)
- jcmd pid VM.flags: 查看JVM的启动参数
- jcmd pid help: 列出当前运行的Java进程可以执行的操作
- jcmd pid help JFR.dump:查看具体命令的选项
- jcmd pid PerfCounter.print:看JVm性能相关的参数
- jcmd pid VM.uptime:查有JVM的启动时长
- jcmd pid GC.class_ histogram: 查看系统中类的统计信息
- jcmd pid Thread.print: 查看线程堆栈信息 == jstack 查看或者是导出 java 应用程序中线程的堆栈信息
- jcmd pid GC.heap dump filename 导出Heap dump文件, 导出的文件可以通过jvisualvm查看
- jcmd pid VM.system_ properties:查看JVM的属性信息
- jcmd pid VM.version: 查看目标 jvm 进程的版本信息
- jcmd pid VM.command_line: 查看 jvm 启动的命令行参数信息
jmc:java mission control jfr:java Filght Recorder