JVM 架构及原理
Date: 2020/8/5 V1.0
Author:Jessie
从一个架构师角度,需要理解事务的本质。工具、技术不断更新迭代,看到深层次的设计思路,是作为架构师必须练习的思路。
JVM构成
JVM由类加载器、运行期数据区、执行引擎组成。运行期数据区包括:方法区、堆、Java栈、程序计数寄存器。
l 类加载器:负责从文件系统加载到class信息,加载的类信息存放入方法区。
l 方法区:存放磁盘加载进来的类字节码。而程序中的创建的类实例存放在堆中。
l 堆:Java堆在虚拟机启动时建立,每个JVM实例唯一对应一个堆。所有的java对象都实例都存放在堆里,并由用用所有的线程共享。
l 堆栈:Java中所有的对象内存空间在堆中分配。但对象的引用在堆栈中分配。即:堆栈中分配的内存只是一个指向这个堆对象的引用。
l Java栈:每个java线程独有的Java栈。随着线程创建而被创建,保存帧的信息。
每当创建一个新的线程时,JVM会为这个线程创建一个Java栈,同时会为这个线程分配一个PC寄存器,并且这个PC寄存器会指向这个线程的第一行可执行代码。每当调用一个新方法时会在这个栈上创建一个新的栈帧数据结构,这个栈帧会保留这个方法的一些元信息,如在这个方法中定义的局部变量、一些用来支持常量池的解析、正常方法返回及异常处理机制等。
l 寄存器:记录下一个可执行的方法。线程独有。
l 执行引擎:把class文件变成本地代码执行。
Java指令集选择栈实现
JVM与实体机一样必须有一套合适的指令集,这个指令集我们称为jvm字节码指令集,符合class规范的字节码都可以被jvm执行,也就是和汇编对应机器码类似的关系,class代码也可以很好地对应jvm指令集。
指令集都会有对应的架构实现,如基于寄存器的架构实现或者基于栈的架构实现,这里的基于寄存器或者栈都是指在一个指令中的操作数是如何存取的。
JVM执行字节码指令是基于栈的架构,也就是所有的操作数必须先入栈,然后根据指令中的操作码选择从栈顶弹出若干个元素进行计算后再将结果压入栈中。在JVM中操作数可以可以存放在每一个栈桢中的一个本地变量集中,即在每个方法调用时就胡igei这个方法分配一个本地变量集,这个本地变量集在编译时就已经确定,所以操作数入栈可以直接是常量入栈或者从本地变量集中取一个变量压入栈中。这和一般的基于寄存器的操作有所不同,一个操作需要频繁地入栈和出栈,如进行一个加法运算,如果连个操作数都在本地变量中,那么一个加法操作就要有5次栈操作,分别是将两个操作数从本地变量入栈(2次入栈操作),再将两个操作数出栈用于加法运算(2次出栈),再将加法结果压入栈顶(1次入栈)。如果是基于寄存器的话,一般只需要将两个操作数存入寄存器进行加法运算后再将结果存入其中一个寄存器即可,不需要那么多的数据移动的操作。那么为什么JVM还要基于栈来设计呢?
JVM为何要基于栈来设计有几个理由。一个是JVM要设计成与平台无关的,而平台无关性就是要保证在没有或者有很少的寄存器的机器上也要同样能正确地执行Java代码。例如,在80X86的机器上寄存器就是没有规律的,很难针对某一款机器设计通用的基于寄存器的指令,所以基于寄存器的架构很难做到通用。
Java字节码文件
文件中的为16进制代码, 文件开头的4个字节称之为 魔数,唯有以"cafe babe"开头的class文件方可被虚拟机所接受,这4个字节就是字节码文件的身份识别。0000是编译器jdk版本的次版本号0,0037是主版本号
Java字节码执行流程及字节码文件编译过程
Java内存泄漏
Java虽然有内存回收机制,但仍然有内存泄漏。这是由于不当的开发引起的。程序中永久不再使用的对象引用。这些对象占用并耗尽内存。
比如:长生命周期对象、静态容器、缓存。常见:一个静态Hashmap中存储数据不释放。
优化资源使用:
l 复用线程或对象资源,避免在程序生命期内创建和删除大量对象
l 池管理算法
l 对象内容置空
l 缩短对象生命周期,加速垃圾回收
l 减少对象驻留内存时间
l 使用是创建对象,用完释放
JVM性能优化工具
JPS、JSTAT、JMAP、JSTACK
JConsole、JVisualVM
JSTAT:对Java应用程序资源和性能进行实时命令行的监控。
JSTACK查看jvm内线程堆栈信息
JConsole
JVisualVM
版权声明: 本文为 InfoQ 作者【架构5班杨娟Jessie】的原创文章。
原文链接:【http://xie.infoq.cn/article/09f4f162c67ed0dc9b1780f32】。文章转载请联系作者。
评论