OOM
!JVM GC问题
,但却无从下手。JVM参数设置
一脸茫然,直接默认吧然后就GG了知乎上有条帖子:应该如何看招聘信息,直通年薪50万+?
面试
的需要(BATJ、TMD,PKQ等面试都爱问)项目管理、调优的需求
垃圾回收算法、JIT(即时编译器)、底层原理
垃圾收集机制
为我们打理了很多繁琐的工作,大大提高了开发的效率,但是,垃圾收集也不是万能的,懂得JVM内部的内存结构、工作机制,是设计高扩展性应用和诊断运行时问题的基础,也是Java工程师进阶的必备能力。英文文档规范:https://docs.oracle.com/javase/specs/index.html
中文书籍:
平台
,Java虚拟机
扮演着举足轻重的作用
文化
,Java几乎成为了“开源”的代名词。
社区
,Java拥有全世界最多的技术拥护者和开源社区支持,有数不清的论坛和资料。从桌面应用软件、嵌入式开发到企业级应用、后台服务器、中间件,都可以看到Java的身影。其应用形式之复杂、参与人数之众多也令人咋舌。(重点)
Java程序
都需要转换成字节码文件
,最后转换的字节码文件都能通过Java虚拟机进行运行和处理
只要其他编程语言的编译结果满足并包含Java虚拟机的内部指令集、符号表以及其他的辅助信息,它就是一个有效的字节码文件,就能够被虚拟机所识别并装载运行
。(重点)
Java字节码
,指的是用Java语言编译成的字节码
。准确的说任何能在jvm平台上执行的字节码格式都是一样的。所以应该统称为:jvm字节码。Java虚拟机
与Java语言
并没有必然的联系,JVM只与特定的二进制文件格式——Class文件格式所关联,Class文件中包含了Java虚拟机指令集(或者称为字节码、Bytecodes)和符号表,还有一些其他辅助信息。多语言混合编程
正成为主流
,通过特定领域的语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。JSR-292
为核心的一系列项目和功能改进(如DaVinci Machine项目、Nashorn引擎、InvokeDynamic指令、java.lang.invoke包等),推动Java虚拟机从“Java语言的虚拟机”向 “多语言虚拟机”的方向发展。EJB规范(太笨重, 后来被Spring取代)
,以及将Java分成了J2EE、J2SE和J2ME。这表明了Java开始向企业、桌面应用和移动设备应用3大领域挺进。Java的默认虚拟机
。Java开源并建立了OpenJDK
。顺理成章,Hotspot虚拟机也成为了OpenJDK中的默认虚拟机。JRockit虚拟机(号称世界上最快的JVM)
。HotSpot
和JRockit
,并计划在未来对它们进行整合:HotRockitJDK7
发布。在JDK1.7u4中,正式启用了新的垃圾回收器G1
。JDK9
发布。将G1设置为默认GC,替代CMS垃圾回收器
JDK11发布
,LTS版本的JDK,发布革命性的ZGC(低延迟, 高吞吐的垃圾回收器(目前最强大的的GC, 还在测试环境下))
,调整JDK授权许可Shenandoah GC
. (OpenJDK中加入了Shenandoah GC)
(重点)
虚拟机(Virtual Machine)
,就是一台虚拟的计算机
。它是一款软件
,用来执行一系列虚拟计算机指令
。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。可运行完整操作系统
的软件平台。Java虚拟机
,它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令我们称为Java字节码指令
。Java虚拟机
是一台执行Java字节码
的虚拟计算机
,它拥有独立的运行机制,其运行的Java字节码也未必由Java语言编译而成。跨平台性、优秀的垃圾回器,以及可靠的即时编译器。
所有的Java程序都运行在Java虚拟机内部。
特点:
一次编译,到处运行
自动内存管理
自动垃圾回收功能
(重点)
HotSpot VM
是目前市面上高性能虚拟机的代表作之一 (目前使用最多, 根正苗红的虚拟机)。执行引擎包含三部分:解释器,即时编译器,垃圾回收器
(重点)
Java虚拟机
所能解释、运行的字节码文件
,那么理论上我们就可以自己设计一套语言了(了解)
栈的指令集架构
,另外一种指令集架构则是基于寄存器的指令集架构
。具体来说:这两种架构之间的区别:指令大部分是零地址指令
,其执行过程依赖于操作数栈
。指令集更小,编译器容易实现可移植性更好,更好实现跨平台
与硬件的耦合度高,可移植性差
一地址指令、二地址指令和三地址指
令为主,而基于栈式架构的指令集却是以零地址指令为主。同样执行2+3
这种逻辑操作,其指令分别如下:
基于栈的计算流程(以Java虚拟机为例):
iconst_2 // 将整数2压入操作数栈
istore_1 // 把栈顶的内容放入局部变量表中索引为 1 的 slot 中
iconst_3 // 将整数3压入操作数栈
istore_2 // 把栈顶的内容放入局部变量表中索引为 2 的 slot 中
iload_1 // 把局部变量表索引为 1 的 slot 中存放的变量值 加载至操作数栈
iload_2 // 把局部变量表索引为 2 的 slot 中存放的变量值 加载至操作数栈
iadd // 栈顶的两个数出栈后相加,结果入栈
istore_0 // 把栈顶的内容放入局部变量表中索引为 0 的 slot 中
return // 方法返回指令,回到调用点
mov eax,2 //将eax寄存器的值设为1
add eax,3 //使eax寄存器的值加3
12
public class StackStruTest {
public static void main(String[] args) {
int i = 2;
int j = 3;
int k = i + j;
}
}
javap -v StackStruTest.class
javap -v StackStruTest.class
反编译得到的指令
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_2 // 将常量 2 压入栈中
1: istore_1 // 将常量 2 保存至变量 i 中
2: iconst_3 // 将常量 3 压入栈中
3: istore_2 // 将常量 3 保存至变量 j 中
4: iload_1 // 加载变量 i
5: iload_2 // 加载变量 j
6: iadd // 执行累加操作
7: istore_3 // 加法结果保存在变量 k 中
8: return
LineNumberTable:
line 10: 0
line 11: 2
line 12: 4
line 22: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
2 7 1 i I
4 5 2 j I
8 1 3 k I
}
跨平台性的设计
,Java的指令都是根据栈
来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令
基于栈的架构跨平台性好、指令集小
,虽然相对于基于寄存器的架构来说,基于栈的架构编译得到的指令更多,执行性能也不如基于寄存器的架构好,但考虑到其跨平台性与移植性,我们还是选用栈的架构
引导类加载器(bootstrap class loader)
创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的。执行Java程序
Java虚拟机的进程
有如下的几种情况:
程序正常执行结束
遇到了异常或错误
而异常终止操作系统出现错误
而导致Java虚拟机进程终止线程调用Runtime类或System类的exit()方法,或Runtime类的halt()方法,并且Java安全管理器也允许这次exit()或halt()操作
。JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载 Java虚拟机时
,Java虚拟机的退出情况。public class StackStruTest {
public static void main(String[] args) {
int i = 2;
int j = 3;
int k = i + j;
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello");
}
}
程序在运行的时候,使用 jps 指令查看当前正在运行的进程,图中 23756 为进程所占用的端口号
StackStruTest 进行执行完毕,就查看不到了
(了解)
Runtime 类源码分析
Runtime(单例) 对象全局唯一
,对应着下图中的【运行时数据区】饿汉单例设计模式
:一上来就 new 了一个 Runtime 类的实例,并且将 Runtime 类的构造器私有化public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class Runtime
are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the Runtime
object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
public void exit(int status) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExit(status);
}
Shutdown.exit(status);
}
ShutDown 类源码分析
exit() 方法
会调用到本地方法 runAllFinalizers() 和halt0()
static void exit(int status) {
boolean runMoreFinalizers = false;
synchronized (lock) {
if (status != 0) runFinalizersOnExit = false;
switch (state) {
case RUNNING: /* Initiate shutdown */
state = HOOKS;
break;
case HOOKS: /* Stall and halt */
break;
case FINALIZERS:
if (status != 0) {
/* Halt immediately on nonzero status */
halt(status);
} else {
/* Compatibility with old behavior:
* Run more finalizers and then halt
*/
runMoreFinalizers = runFinalizersOnExit;
}
break;
}
}
if (runMoreFinalizers) {
runAllFinalizers();
halt(status);
}
synchronized (Shutdown.class) {
/* Synchronize on the class object, causing any other thread
* that attempts to initiate shutdown to stall indefinitely
*/
sequence();
halt(status);
}
}
static void halt(int status) {
synchronized (haltLock) {
halt0(status);
}
}
static native void halt0(int status);
/* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
private static native void runAllFinalizers();
System 类源码分析
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
总结
System.exit()
方法 --> 调用 Runtime.exit()
方法Runtime.exit()
方法 --> 调用了 ShutDown.exit()
方法(了解)
HotSpot历史
目前Hotspot占有绝对的市场地位,称霸武林。
默认的虚拟机都是HotSpot
相关机制也主要是指HotSpot的GC机制
。(比如其他两个商用虚机都没有方法区的概念)从服务器、桌面到移动端、嵌入式都有应用。
名称中的HotSpot指的就是它的热点代码探测技术。
通过计数器找到最具编译价值代码,触发即时编译或栈上替换
通过编译器与解释器协同工作,在最优化的程序响应时间与最佳执行性能中取得平衡
JRockit JVM是世界上最快的JVM
:使用JRockit产品,客户已经体验到了显著的性能提高(一些超过了70%)和硬件成本的减少(达50%)。有影响力的三大商用虚拟机之一,也号称是世界上最快的Java虚拟机
。AliJVM团队
发布。阿里,国内使用Java最强大的公司,覆盖云计算、金融、物流、电商等众多领域,需要解决高并发、高可用、分布式的复合问题。有大量的开源产品。主要以Oracle HotSpot VM为默认虚拟机。