查看: 168|回复: 0

JVM内存模型

[复制链接]
发表于 2020-2-18 10:29:47 | 显示全部楼层 |阅读模式
个人博客
http://www.milovetingting.cn
JVM内存模型

媒介

本文为学习Java相干知识所作条记,参考以下资料:https://github.com/Snailclimb/JavaGuide ,感谢原作者的分享!
基本概念
JVM是可运行Java代码的虚拟盘算机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收器、堆和一个存储方法域。JVM运行在操作体系之上,它与硬件没有直接交互。
Java源文件,通过编译器,能够生产相应的class文件,也就是字节码文件,字节码文件通过Java虚拟机中的解释器,编译成特定呆板上的呆板码。
Java源文件-->编译器-->字节码文件
字节码文件-->JVM-->呆板码
每一种平台的解释器是差别的,但是实现的虚拟机是相同的,这也就是 Java 为什么能够跨平台的原因了 ,当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享。

线程

这里所说的线程指程序执行过程中的一个线程实体。JVM 允许一个应用并发执行多个线程。Hotspot JVM 中的 Java 线程与原生操作体系线程有直接的映射关系。当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作体系原生线程。Java 线程结束,原生线程随之被回收。操作体系负责调度所有线程,并把它们分配到任何可用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。当线程结束时,会释放原生线程和 Java 线程的所有资源。
Hotspot JVM 后台运行的体系线程主要有下面几个:

  • 虚拟机线程(VM thread):
    这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,由于当堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的范例有:stop-the-world 垃圾回收、线程栈 dump、线程停息、线程方向锁(biased locking)排除。
  • 周期性任务线程: 这线程负责定时器事件(也就是停止),用来调度周期性操作的执行。
  • GC 线程: 这些线程支持 JVM 中差别的垃圾回收运动。
  • 编译器线程: 这些线程在运行时将字节码动态编译成本地平台相干的呆板码。
  • 信号分发线程: 这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理。
JVM内存地区


JVM 内存地区主要分为线程私有地区【程序计数器、虚拟机栈、本地方法区】、线程共享地区【JAVA 堆、方法区】、直接内存。

  • 线程私有数据地区生命周期与线程相同, 依靠用户线程的启动/结束 而 创建/销毁(在 Hotspot VM 内, 每个线程都与操作体系的本地线程直接映射, 因此这部门内存地区的存/否跟随本地线程的生/死对应)。
  • 线程共享地区随虚拟机的启动/关闭而创建/销毁。
  • 直接内存并不是 JVM 运行时数据区的一部门, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于 Channel 与 Buffer 的 IO 方式, 它可以使用 Native 函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 如许就避免了在 Java堆和 Native 堆中往返复制数据, 因此在一些场景中可以显著进步性能

程序计数器(线程私有)

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

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
栈帧( Frame)是用来存储数据和部门过程结果的数据布局,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
本地方法区(线程私有)

本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务, 假如一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个C 栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一。
堆(Heap-线程共享)-运行时数据区

是被线程共享的一块内存地区,创建的对象和数组都保存在 Java 堆内存中,也是垃圾网络器进行垃圾网络的最重要的内存地区。由于现代 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 文件的每一部门(自然也包括常量池)的格式都有严酷的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,如许才会
被虚拟机认可、装载和执行。
JVM 运行时内存

Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。

新生代

是用来存放新生的对象。一样平常占据堆的 1/3 空间。由于频繁创建对象,以是新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden 区ServivorFrom、ServivorTo 三个区。
Eden 区

Java 新对象的出生地(假如新创建的对象占用内存很大,则直接分配到老
年代)。当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行
一次垃圾回收。
ServivorFrom

上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
ServivorTo

保留了一次 MinorGC 过程中的幸存者。
MinorGC 的过程(复制->清空->交换)

MinorGC 采用复制算法。

  • eden、servicorFrom 复制到 ServicorTo,年龄+1
首先,把 Eden 和 ServivorFrom 地区中存活的对象复制到 ServicorTo 地区(假如有对象的年龄以及到达了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(假如 ServicorTo 不够位置了就放到老年区);

  • 清空 eden、servicorFrom
然后,清空 Eden 和 ServicorFrom 中的对象;

  • ServicorTo 和 ServicorFrom 交换
末了,ServicorTo 和 ServicorFrom 交换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom区。
老年代

主要存放应用程序中生命周期长的内存对象。
老年代的对象比力稳定,以是 MajorGC 不会频繁执行。在进行 MajorGC 前一样平常都先进行了一次 MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到充足大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间。
MajorGC 采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC 的耗时比力长,由于要扫描再回收。MajorGC 会产生内存碎片,为了减少内存消耗,我们一样平常需要进行归并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出 OOM(Out of Memory)异常。
永久代

指内存的永久保存地区,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被放入永久地区,它和和存放实例的地区差别,GC 不会在主程序运行期对永久地区进行清理。以是这也导致了永久代的地区会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。
JAVA8 与元数据

在 Java8 中,永久代已经被移除,被一个称为“元数据区”(元空间)的地区所取代。元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的巨细仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中,如许可以加载多少类的元数据就不再由MaxPermSize 控制, 而由体系的实际可用空间来控制。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?用户注册

x

天涯海角也要找到Ni:JVM内存模型

中发现Ni: JVM内存模型
中发现Ni: JVM内存模型
中发现Ni: JVM内存模型
中发现Ni: JVM内存模型
中发现Ni: JVM内存模型
中发现Ni: JVM内存模型
相关技术服务需求,请联系管理员和客服QQ:2753533861或QQ:619920289
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

帖子推荐:
客服咨询

QQ:2753533861

服务时间 9:00-22:00

快速回复 返回顶部 返回列表