用最简单的原理图深入剖析Jvm内存模型

作为一个编程人员,对Jdk应该是相当熟悉了,不知大家有没有留意,当安装好Jdk后,其bin目录下会有两个重要的exe文件:javac.exe(编译器)和java.exe(.class文件执行器)。其实Java最大的优点在于跨平台,原因恰恰就是因为Jdk分不同版本,比如windows版本和Linux版本,针对同一个.java源码文件,其通过编译器(javac.exe)编译成JVM文件(.class文件)的字节码是不一样的,而字节码文件通过java.exe执行,就能成为机器码。

简单总结,就是Java源文件,通过编译器,能够生产相应的.class文件(字节码文件),而字节码文件又通过Java虚拟机中的解释器,编译成特定机器上的机器码。
①Java源文件-->编译器-->字节码文件
②字节码文件-->JVM-->机器码

理解完这一个过程以后,我们就可以讲讲JVM的内存模型结构,只有理解了内存模型结构,才能真正理解Jvm调优。根据JVM规范,JVM内存共分为栈(线程)、本地方法栈、程序计数器、堆和方法区(元空间)五个部分

栈,也称虚拟机栈(线程私有)

是描述java方法执行的内存模型,每个方法在执行的时候会创建一个栈帧(Stack Frame),用于存储局部变量,操作数栈,动态链接,方法返回地址(方法出口)等。每个方法从调用到执行完成的过程,对应一个栈帧在虚拟机栈中的入栈到出栈的过程。
通常所说的栈,一般是指在虚拟机栈中的局部变量部分。

局部变量所需内存在编译期间完成分配,如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。

本地方法栈(线程私有)

和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。

程序计数器(线程私有)

一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法,则为空。这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。

堆(线程共享)

被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例(new girlFriend)和数组等,是垃圾收集器进行垃圾收集的最重要的内存区域。

当堆中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。由于现代VM采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代( Eden 区 、 From Survivor 区 和 To Survivor 区 )和老年代。

方法区,也叫永久代(线程共享)

即我们常说的永久代(Permanent Generation), 用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,是被所有方法线程共享的一块内存区域。

HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,而不必为方法区开发专门的内存管理器(永久代的内存回收的主要目标是针对常量池的回收和堆类型的卸载, 因此收益一般很小)。

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。

如何理解线程私有和线程共享区?
线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束而创建/销毁(在Hotspot VM内, 每个线程都与操作系统的本地线程直接映射, 因此这部分内存区域的存/否跟随本地线程的生/死对应)。

线程共享区域随虚拟机的启动/关闭而创建/销毁。

补充:直接内存并不是JVM运行时数据区的一部分, 但也会被频繁的使用: 在JDK1.4引入的NIO提供了基于 Channel与Buffer的IO方式, 它可以使用Native函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: JavaI/O扩展), 这样就避免了在Java堆和Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。

发布了224 篇原创文章 · 获赞 34 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_39309402/article/details/104665156
今日推荐