一、学习JVM可以干什么:
- 防止内存泄漏(Memory leak),防止内存溢出(Out of Memory);
- 了解线程锁的工作原理,进而优化线程锁的使用 (Thread Lock)提高性能;
- 科学进行垃圾回收 (Garbage collection);
- 提高系统吞吐量 (throughput);
- 降低延迟(Delay),提高其性能(performance)
二、字节码底层执行过程:
- JVM使用内置的javac指令机制将java对象编译为.class文件;
- 再讲.class文件转化为0101的二进制文件交给操作系统执行;
二、在主流的 JVM(例如 HotSpot)中,将 class 文件翻译成机器码执行时提供了两种方式:
- 传统方式(解释执行器方式): 解释执行(边编译边执行);
优点: 占用内存资源少,省去编译的时间,迅速执行;
缺点: 边编译边执行,影响程序性能; - 非传统方式(编译执行器JNT方式): 编译执行(将热点代码编译后存入缓存,下次执行从缓存取,提高性能,热点代码指循环或高频使用的方法);
优点: 提高了程序执行的性能;
缺点: 占用内存资源多,程序开始执行时编译耗时;
三、JVM的三大组成部分:
- 类加载系统 (ClassLoader System): 负责加载类到内存;
- 运行时数据区 (Runtime Data Area): 负责存储数据信息,包括:
共享:方法区,堆内存区;
私有:栈内存区,本地栈内存区,程序计数器; - 执行引擎 (Execution Engine): 负责调用对象执行业务,包括:解释执行器,编译执行器(JNT)和GC;
四、JVM运行时内存结构:
1. Heap (堆内存):
堆内存概要:
- 虚拟机启动时创建,被所有线程共享, 用于存放所有 java 对象实例.;
- 可分为年轻代(伊甸园区eden,From和To三部分)和老年代(Tenured);
- 是垃圾收集器(GC)管理的主要区域 ;
- 堆中没有内存分配时将会抛出 OutOfMemoryError
对象内存分配说明:
- 新创建的对象一般都分配在年轻代,当对象比较大时年轻代没有足够空间还可直接分配到老年代。有时候系统为了减少GC开销对于小对象且没有逃逸的对象还可以直接在栈上分配。
对象分配总结:
- 一般分配在堆内存的新生代区;
- 大对象新生代空间不足时,可直接分配到老年代区;
- 小对象且没有逃逸的对象可直接分配到栈内存(逃逸指对象被外界引用);
//逃逸
Object obj;
public void method(){
obj = new Object();
}
//没有逃逸
public void method(){
Object obj = new Object();
}
- 堆内存大小参数配置:
- -Xms 设置堆的最小空间大小;
- -Xmx 设置堆的最大空间大小;
- -XX:NewSize 设置新生代最小空间大小;
- -XX:MaxNewSize 设置新生代最大空间大小;
- -XX:NewRatio 新生代和老年代的比值,值为4则表示新生代:比老年代1:4;
- -XX:SurivorRatio 表示 Survivor 和 eden 的比值,值为 8 表示两个;
- survivor:eden=2:8;
- -Xss:设置每个线程的堆栈大小.
2. Method Area (方法区,也称作非堆内存)
方法区概要:
- 非堆内存,逻辑上的定义,用于存储类的数据结构(类结构)信息;
- 不同 jdk,方法区的实现不同,JDK8 中的方法区对应的是 Metaspace,是一块本地内存.
方法区内存配置参数说明:
- -XX:PermSize 设置永久代最小空间大小(JDK8 已弃用)。
- -XX:MaxPermSize 设置永久代最大空间大小(JDK8 已弃用)。
- -XX:MetaspaceSize 设置元数据区最小空间。 (JDK8)
- -XX:MaxMetaspaceSize 设置元数据区最大空间。(JDK8)
3. Program Counter Register(程序计数器)
- 线程启动时创建,线程私有;
- 用于记录当前正在执行的虚拟机字节码指令地址;
- Java 虚拟机规范唯一一个没有内存溢出的区域.
4. Stack Area (虚拟机栈区)
虚拟机栈概要:
- 用于存储栈帧(Stack Frame)对象,保存方法的局部变量表、操作数栈、执行运行时常量池的引用和一些额外的附加信息;
- 一次方法调用都会创建一个新的栈帧,并压栈。当方法执行完毕之后,便会将栈帧出栈。
- 栈上分配: 对于小对象()一般几十个 bytes),在没有逃逸的情况下,可以直接分配在栈上(直接分配在栈上,可以自动回收,减轻 GC 压力),大对象或者逃逸对象无法栈上分配
说明: 方法在进行递归调用时容器出现栈内存溢出
虚拟机栈参数说明:
- -Xss128k: 设置每个线程的堆栈大小
JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右。
5. Native Stack Area (本地方法栈)
- 为虚拟机使用到的 Native 方法(C/C++代码)提供服务;
- 用于存储本地方法执行时的一些变量信息,操作数信息,结果信息