(学习笔记)jvm虚拟机模型

Java内存管理机制

java由于在虚拟机的自动管理机制下,可以不需要管理内存,提升了程序员跟多时间在业务逻辑中,但是对内存管理的封装,也导致了当出现内存泄露等问题时,修正问题是一个艰难的工作。

java运行时的数据区域

java所管理的内存将会按照以下几个区域进行划分:
image

  • 程序计数器
    程序计数器是一小块内存空间,其大小可以忽略不计,他是程序控制流的指示器,分支,循环,跳转,异常处理,县城回复等基础功能都需要依赖这个计数器。
    在多线程中,由于在某个时刻,一个处理器(或者一个内核)都只会执行一个线程,因此为了线程切换后可以回复到正确的位置。
    每个线程有独力的计数器,互不影响,独立存储,我们将这类内存区域称之为线程私有的内存。

  • Java 虚拟机栈
    与程序计数器一样,虚拟机栈也是线程私有的,生命周期与线程相同。
    虚拟机栈的生命周期与县城乡同,描述的java方法被执行的线程内存模型:每个方法被执行的时候mjava虚拟机都会同步创建一个栈帧,用于存储局部吧变量表、操作数栈、动态连接、方法出口等信息,一个虚拟机调用方法并执行完毕,对应着一个栈帧在虚拟机栈中入栈到出站的过程。
    局部变量表主要存放的是java的基本数据类型(boolean,byte、char、short、int、float、double、),对象引用(reference)与 returnAddress类型。
    这些局部变量在局部变量表中的存储空间用局部变量槽表示。
    -虚拟机栈中出现的两种异常:
    1 线程请求的栈深度大于虚拟机允许深度,抛出StackOverflowError
    2 虚拟机栈容量可以动态扩展,但是扩展是无法扩展时抛出OOM异常。

  • 本地方法栈
    本地方法栈与虚拟机栈功能类似,到那时虚拟机栈中执行的是java的方法,本地方法栈执行的是本地(Native)方法。

  • java堆
    java 是虚拟机中内存管理中最大的一块,是所有线程共享的一块内存区域,在虚拟机启动时创建,java中“几乎”所有的对象实例都在堆中。
    java堆是垃圾收集器管理的内存区域,GC堆。(以前,不少书中仍然人物java的堆内存分为新生代,老年代,永久代,等)
    java堆可以划出多个线程私有的分配缓冲区(TLAB)提高分配效率。
    java堆可以出现于不连续的物理上的内存空间中,但是其逻辑上是连续的。
    主流的虚拟机堆是通过(-Xmx 与-Xms)大小的。如果堆中内衣实例分配,或者堆无法继续扩展时,将会抛出oom异常。

  • 方法区
    方法区与java堆一样,都是线程共享的内存区域,用于储存加载的类型信息,常量,静态常量,及时编译器编译后的代码缓存等数据。
    方法区无法满足新的内存分配需求时,将抛出oom异常。

  • 运行时常量池
    运行时常量池是方法区的一部分,class文件中除了有类的版本、字段、方法、还有一个是常量池表,用于存放编译期生成的各种字面量与符号引用。
    运行是常量池中北使用较多的是String的intern()方法,因为,通过String s=“xxx”赋值的s是首先在运行时常量池中查找是否有“xxx”,如果没有则创建一个空间来储存,如果有则直接在栈内存中开辟一个s的空间,来存储“xxx”的地址。
    而通过new String(“xxx”)的方法是直接在堆中创建的对象。
    运行时常量池是方法区的一部分自然收到方法区内存限制 ,无法申请内存是抛出oom异常。

HotSpot虚拟机对象揭秘

  • 对象的创建
    当虚拟机遇到一条字节码的new命令时,首先将去检查这个指令是否以及在常量池中 可以定位到一个类的符号引用,并且检查引用带边的类是否被加载、解析与初始化过。如果没有,就将执行相应的类加载过程。
    类检查通过后将进行给新生的对象分配内存,内存的大小在类加载后就可以确定。就是将一块确定大小的内存从java堆中划分出来。采用指针碰撞的方式。
  • 解决对线创建在并发情况下的线程安全,通过的是两个方法

(1)对分配的内存空间动作进行同步处理--实际上虚拟机采用的是CAS配上失败重试的方式来保证更新操作的原子性的。
(2) 另外一个是将内存分配的动作按照线程划分为不同的空间进行,预分配空间给每个线程,即本地缓冲区(TLAB)。

  • 内存分配完毕后将初始化零值
  • 对象的创建内存布局
    对象可以划分为三个部分,对象头,实例数据,对其填充。
    HotSpot虚拟机对象的对象头包括两类信息:
    (1) 是储存对象自身运行时数据,汝hashCode、GC分带年龄、锁状态的标志等。
    (2) 另外一部分是类型指针,通过指针来确定该对线是哪个类的实例。
  • 对象的访问定位
    对象的访问方式有句柄访问与直接指针两种。
    (1) 句柄访问时在java堆中划分出一块内存做句柄池,referecne是对象的句柄地址,句柄中包含对线的实例数据与类型数据各自的地址。
    (2) 直接指针是referece中储存的直接是对线的地址,减少一次访问开销。

image
image
(图片转自https://blog.csdn.net/qq_41901915/article/details/88956143)

总结

posted @ 2020-05-29 14:35  搞材料的小周  阅读(323)  评论(0编辑  收藏  举报