简单说说jvm的对象存储与访问

简单说说jvm的对象存储与访问_第1张图片
JDK技术组成.png

图上所涉及的是Java的技术组成,确实,没有必要全部深入了解。但是大致的框架是有必要明确的。业务层的程序员可能并不了解这些,所以我还是弄一篇笔记用来学习和分享

传统意义上,Java包括了:

  • Java程序设计语言
  • 各种平台上的Java虚拟机(JVM)
  • class文件格式
  • Java API
  • 第三方Java库

jvm是Java最小运行环境,JDK是Java最小开发环境

想知道对象怎么创建的,就要先知道,JVM的内存管理,先介绍一下

内存管理

Java比C++多了一个内存管理,减少了程序员对内存回收的操作,方便的同时也带来了新的麻烦,所以我们需要了解Java的内存管理技术,避免过于浪费性能。

简单说说jvm的对象存储与访问_第2张图片
Java虚拟机运行时数据区
  • 程序计数器
    标记当前线程程序执行到的位置,这样可以方便线程切换时恢复到正确的执行位置。所以计数器是线程私有数据。
  • 虚拟机栈
    初学Java的时候,老师喜欢把Java内存模型分为堆内存和栈内存,栈内存就是这玩意儿,是Java方法执行的内存模型,每个方法执行的时候都会创建一个栈帧(Stack Frame) 存储局部变量,操作数栈,动态链接,方法出口等。
    所以堆内存和栈内存的分类是比较粗糙的,实际上要复杂的多。
    在虚拟机中,如果线程请求的深度大于栈的深度就会出现StackOverflowError错误,如果是动态扩展的栈,当扩展无法获得足够的内存会抛出OutOfMemoryError错误
  • 本地方法栈
    执行Native方法的服务,具体看JVM的实现,方便程序调用本地的方法与服务。
  • Java堆
    Java堆是被一群线程共享是一块内存,应该是最大的一块存储空间了。在虚拟机启动时创建,GC管理的最大的一块地方。这里可以划分出多个线程私有的缓冲区,但是这些划分与存放内容无关,这里主要存的,是对象。当然,这里是可拓展的。
  • 方法区
    存放虚拟机加载的类信息,常量,静态变量,即时编译的代码数据。
  • 运行时常量池
    是方法区的一部分,Class文件除了类版本,字段,方法,接口等描述信息外,还有一项是常量池,存放编译时,生成的各种字面量,符号引用,这部分内容将在类加载后进入方法区的运行时常量池内

上面说了这么多,那Java是怎么创建对象的呢?

对象的创建

当虚拟机获得new指令的时候,首先会在常量池中找到类符号引用,检查符号是否被加载、解析、初始化过,检查通过以后执行未执行的操作,然后虚拟机会为新对象分配内存(把一块确定大小的内存从堆中分出来)。
在规整的Java堆内存中(用过的内存放在一边,没用过的内存放在另一边,中间靠一个指针来分开),分配的内存就是将指针移动一下,让出对象对应大小的位置。而在非规整的堆内存中,空闲与非空闲的相互交错,这时候,Java会维护一个列表,记录内存可用的区块,分内存给对象的时候,就是找到一块足够大的地方,存下这个对象,并更新记录表。不同的JVM有不同的记录方式。

这样分配如何保证线程安全呢,即使在仅仅改变内存指针的行为,在并发情况下,也不是安全的,可能线程A正在给对象分配内存,还没来得及修改,而线程B却使用了老的指针地址。这样,要么对修改指针做同步处理,要么把内存分配动作按线程划分在不通空间(本地线程分配缓冲)。分配完,对内存进行清零,让后对对象进行初始化。将对象的哈希码,GC分代年龄存放在对象头中。

对象的访问定位

访问对象要靠栈上的reference数据寻址。Java没有定义这个引用如何定位,所以有两种方式访问。(句柄访问,指针访问)
句柄访问:栈reference中存储的是句柄的地址,对应的堆的句柄池内的数据。如图:

简单说说jvm的对象存储与访问_第3张图片
句柄访问简单示意图

指针访问:指针直接访问对象


简单说说jvm的对象存储与访问_第4张图片
指针访问简单示意图

句柄访问存储比较稳定,对象移动的时候只需要改变指针内的地址,reference内的数据不用改变,指针直接访问速度更快,节省开销。

虽然Java有GC,但是GC不是万能的,内存溢出还是会存在。所以应该说了解JVM,应该也是是Java程序员的必修课之一吧。

你可能感兴趣的:(简单说说jvm的对象存储与访问)