Jvm原理解析

什么是Jvm?

  • 翻译机

首先,你可以把Java虚拟机看作一个抽象的计算机,它有各种指令集和各种运行时数据区域。它就是一台翻译机器,将生成的字节码,翻译成各个操作系统能懂的指令。

  • 跨语言、跨平台

JVM是JRE(Java Runtime Environment)的一个组成部分。
虽然叫Java虚拟机,但其实在它之上运行的语言可不仅仅是Java,还包括Kotlin、Groovy等。

Java虚拟机执行流程分为两大部分,分别是编译时环境和运行时环境,当一个Java文件经过Java编译器编译后会生成Class文件,这个Class文件会由Java虚拟机来进行处理。Java虚拟机与Java语言没有什么必然的联系,它只与特定的二进制文件:Class文件有关。因此无论任何语言只要能编译成Class文件,就可以被Java虚拟机识别并执行。

而Jvm可以跑在Linux、Windows、MacOS上,是可以跨平台的。

Class文件格式是怎样的?

示例如图:


可以看到ClassFile具有很强的描述能力,将类文件的各种常量,接口,字段,方法,属性都记录下来了,其中u4、u2表示“基本数据类型”。

  • u1:1字节,无符号类型。
  • u2:2字节,无符号类型。
  • u4:4字节,无符号类型。
  • u8:8字节,无符号类型。
Jvm如何读取java语言编写的程序?

Java虚拟机内部,有一个叫做类加载器的子系统,这个子系统用来在运行时根据需要加载类。详情可见我的另一篇类加载器原理的文章。

Jvm的运行过程:

Jvm核心——运行时数据区:

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为为若干不同的数据区域,包括方法区、堆区、虚拟机栈、本地方法栈、程序计数器。

  • 程序计数器(非线程共享)

指当前线程正在执行的字节码指令的地址。
我们写一个类,创建一个实例对象,调用一个方法。我们build之后,编译成这个类的class文件。


javap反编译这个文件之后,得到这个方法的偏移量,这个就是程序计数器记录字节码的地址。

为什么Jvm需要一个程序计数器的东西?
因为程序执行到方法的某一行时,由于多线程的执行,另一个线程调度了,这个线程要休息了,就突然停在这一行代码。等系统调度再度执行这个方法时,不会又从第一行执行啊,肯定是接着之前的行号执行,那么这个记录就是程序计数器来做的事情。

  • 虚拟机栈(非线程共享)

常常听到说,Jvm是基于栈的,那为什么会这么说呢?为什么程序会按照我们所写的执行顺序去执行呢?因为Jvm会将一个线程中要执行的一个个方法按照顺序,压入到虚拟机栈中。创建线程的时候,会对应创建一个虚拟机栈,会把该线程需要执行的方法按先后人栈,这个方法入栈时就是栈帧。

用于存储当前线程运行方法所需的数据,指令,返回地址。虚拟机栈是线程私有的,各自线程有各个线程的虚拟机栈。

栈帧:

存放的内容包括局部变量表、操作数栈、动态连接、完成出口,对应记录了方法内的局部变量,操作数,多态,方法返回等信息。

虚拟机栈也有大小限制,用-Xss表示。


总结:一个应用会有多个线程,一个线程对应一个虚拟机栈,一个虚拟机栈中会有多个栈帧,一个栈帧即为一个要执行的方法。用栈的弹出方式,所以能让程序有序地执行。就像压入子弹一样,按照顺序一颗一颗地打出去。

  • 本地方法栈(非线程共享)

本地方法栈保存的是native方法的信息。当一个Jvm创建的线程调用native方法后,Jvm不再为其在虚拟机中创建栈帧,只是简单地动态链接并直接调用native方法。

  • 方法区(线程共享)

存放包括类信息、常量、静态变量、即时编译期编译后的代码。(线程共享)

  • 堆区(线程共享)

几乎所有的对象实例都是存储在堆区中的,数组也是存储在堆区中。

为什么设计上将变量存储分为2块区域呢?因为对象、数组这些需要大块空间的边变量,需要频繁创建和回收,而常量、静态变量这些很少回收且不便于回收,所以划分了2块区域各自存储。

堆区也有大小限制,用-Xmx表示。比如声明2G大小堆区:

org.gradle.jvmargs=-Xmx2048m -noverify
Jvm运行时数据区过程总结:

1.申请内存:先要去将堆、栈、方法区等所需的空间大小申请出来。
2.类加载: 将class类中的常量、静态变量、对象的引用等加载进方法区。
3.入栈:将类中的方法按顺序压入虚拟机栈。

  1. 将new的对象存入堆区,栈帧中对象的引用指向堆中的对象地址。
StackOverflowError出现的原因及解决

一般出现这个问题是因为程序里有死循环调用方法或递归调用方法所产生的。
示例:

class MainActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.R)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        add(0)
    }

    private fun add(i: Int) {
        var i = i
        i++
        Log.i("minfo", "i=$i")
        add(i)
    }
}

调用add()方法的时候,会对add()方法进行压栈操作,将add()运行期数据的数据集保存到栈帧中。
add()递归调用时,都会产生一个新的栈帧区块,这是就会连续的产生新的栈帧区块。当栈内存超过系统配置的栈内存,就会出现java.lang.StackOverflowError异常。

什么是逃逸分析

逃逸分析一种数据分析算法,基于此算法可以有效减少 Java 对象在堆内存中的分配。Hotspot 虚拟机的编译器能够分析出一个新对象的引用范围,然后决定是否要将这个对象分配到堆上。

使用逃逸分析,编译器可以对代码做如下优化:
栈上分配:将堆分配转化为栈分配。如果一个对象在方法内创建,要使指向该对象的引用不会发生逃逸,对象可能是栈上分配的候选。
同步省略:又称之为同步锁消除,如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 寄存器中。

示例:
    public static void main(String[] args) {
        while (true) {
            Integer integer = new Integer(111111111);
        }
    }
将堆内存分配减小,并配置关闭逃逸分析
-XX:+DoEscapeAnalysis : 表示开启逃逸分析

-XX:-DoEscapeAnalysis : 表示关闭逃逸分析

jdk1.6默认逃逸分析是开启的,如果关闭逃逸分析,那么在这么频繁分配对象的情况下,GC会频繁地进行回收对象。当开启逃逸分析,那么该对象发觉会引起频繁的回收,那么会将该对象分配到栈中,因为在栈上分配的对象在方法执行完成之后,会自行清理,减小了垃圾回收器的压力。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容