https://www.cnblogs.com/chanshuyi/p/jvm_serial_04_from_source_code_to_machine_code.html
1、编译器和解释器,源代码到字节码再到机器码
编译器种类.pngJIT编译器(Just In Time Compiler)
image.png当源代码转化为字节码之后,其实要运行程序,有两种选择。一种是使用 Java 解释器解释执行字节码,另一种则是使用 JIT 编译器将字节码转化为本地机器代码。
这两种方式的区别在于,前者启动速度快但运行速度慢,而后者启动速度慢但运行速度快
。至于为什么会这样,其原因很简单。因为解释器不需要像 JIT 编译器一样,将所有字节码都转化为机器码,自然就少去了优化的时间。而当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。所以在实际情况中,为了运行速度以及效率,我们通常采用两者相结合的方式进行 Java 代码的编译执行。
在 JVM 中有三个非常重要的编译器,它们分别是:
前端编译器
、JIT 编译器
、AOT 编译器
。
前端编译器,最常见的就是我们的 javac 编译器,其将 Java 源代码编译为 Java 字节码文件。JIT 即时编译器,最常见的是 HotSpot 虚拟机中的 Client Compiler 和 Server Compiler,其将 Java 字节码编译为本地机器代码。而 AOT 编译器则能将源代码直接编译为本地机器码。这三种编译器的编译速度和编译质量如下:
编译速度上,解释执行 > AOT 编译器 > JIT 编译器
。
编译质量上,JIT 编译器 > AOT 编译器 > 解释执行
。
而在 JVM 中,通过这几种不同方式的配合,使得 JVM 的编译质量和运行速度达到最优的状态。
2、字节码结构
https://www.cnblogs.com/chanshuyi/p/jvm_serial_05_jvm_bytecode_analysis.html
Class文件字节码结构组织示意图.png3、类加载执行步骤
JVM 虚拟机执行 class 字节码的过程可以分为七个阶段:加载、验证、准备、解析、初始化、使用、卸载。
4、JVM参数设置
https://blog.csdn.net/longgeqiaojie304/article/details/93851827
【1】Boolean类型XX参数
公式:-XX:+ 或者-XX:- 某个属性值(+表示开启,-表示关闭)
案例:
1)是否打印GC收集细节
-XX:+PrintGCDetails
-XX:-PrintGCDetails
2)是否使用串行垃圾收集器
-XX:+UseSerialGC
-XX:-UserSerialGC
【2】KV设值类型
公式:-XX: key(属性)= value(属性值)
案例:
查看-XX:MetaspaceSize=默认值大小
【3】如何解释-Xms和-Xmx参数属于XX参数
-Xms和-Xmx两个经典参数看起既不像Boolean类型XX参数,也不像KV设值类型XX参数。那为什么-Xms和-Xmx又属于XX参数?
-Xms = -XX:InitialHeapSize
-Xmx = -XX:MaxHeapSize
5、JVM堆内存分配
https://my.oschina.net/u/2984281/blog/784027
Jvm区域总体分两类,
heap区
和非heap区
。heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)
年轻代(Young Gen)
:年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁,年轻代分成1个Eden Space和2个Suvivor Space
年老代(Tenured Gen)
:年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁(譬如可能几个小时一次)
持久代(Perm Gen)
:持久代主要存放类定义、字节码和常量等很少会变更的信息
6、重载:
在Java语言中,字段是无法重载的
。两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称;
在Class文件中,如果两个字段的描述符不一致,那字段重名就是合法的。
在Java语言
中,要重载
一个方法,除了要与原方法具有相同的简单名称
之外,还要求必须拥有与原方法不同的特征签名
。特征签名是指,一个方法中各个参数在常量池中的字段符号引用的集合。也就是因为返回值不会包含在特征签名中
,因此Java语言里面是无法仅仅依靠返回值的不同来对一个已有方法进行重载的;
而在Class文件
中,特征签名的范围更大,只要描述符不是完全一致的两个方法也可以共存。也就是说,如果两个方法有相同的名称和特征签名,但返回值不同
,那么也是可以合法共存于同一个Class文件中的。
7、Class文件中的Code属性
Java程序
方法体
中的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性
内。Code属性出现在方法表的属性集合中,但不是所有的方法表都必须存在这个属性,譬如接口或者抽象类中的方法。
任何实例方法
中(static方法除外),都可以通过“this”关键字访问到此方法所属的对象。其实现是:通过在javac编译器编译的时候把对this关键字的访问转变为对一个普通方法参数的访问
,然后在虚拟机调用实例方法时自动传入此参数。因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留出第一个Slot位来存放对象实例的引用
,方法参数值从1开始计算。
8、Full GC和Minor GC、Major GC
https://www.cnblogs.com/chanshuyi/p/jvm_serial_10_gc_type.html
从年轻代空间回收内存被称为
Minor GC
,有时候也称之为 Young GC。对于 Minor GC,你需要知道的一些点:
(1)当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以 Eden 区越小,越频繁执行 Minor GC。
(2)当年轻代中的 Eden 区分配满的时候,年轻代中的部分对象会晋升到老年代,所以 Minor GC 后老年代的占用量通常会有所升高。
质疑常规的认知,所有的 Minor GC 都会触发 Stop-The-World,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的,因为大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果情况相反,即 Eden 区大部分新生对象不符合 GC 条件(即他们不被垃圾回收器收集),那么 Minor GC 执行时暂停的时间将会长很多(因为他们要JVM要将他们复制到 Survivor 区或老年代)。
从老年代空间回收内存被称为
Major GC
,有时候也称之为 Old GC。
许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。
Minor GC 作用于年轻代
,Major GC 作用于老年代
。 分配对象内存时发现内存不够,触发 Minor GC。Minor GC 会将对象移到老年代中,如果此时老年代空间不够,那么触发 Major GC。因此才会说,许多 Major GC 是由 Minor GC 引起的。
Full GC
是清理整个堆空间 —— 包括年轻代、老年代和永久代(如果有的话)。因此 Full GC 可以说是 Minor GC 和 Major GC 的结合。
当准备要触发一次 Minor GC 时,如果发现年轻代的剩余空间比以往晋升的空间小,则不会触发 Minor GC 而是转为触发 Full GC。因为JVM此时认为:之前这么大空间的时候已经发生对象晋升了,那现在剩余空间更小了,那么很大概率上也会发生对象晋升。既然如此,那么我就直接帮你把事情给做了吧,直接来一次 Full GC,整理一下老年代和年轻代的空间。
网友评论