深刻理解java虚拟机第二版(二)java内存区域与内存溢出异常

JVM运行时数据区域;HostSpot虚拟机对象建立(java堆给对象分配内存的方式,解决对象内存分配的线程安全方案,对象内存布局,对象的访问定位,异常实例);java

1.JVM运行时数据区域:

  • 程序计数器(Program Counter)
  • Java虚拟机栈(Java Virtual Machine Stack)
  • 堆(Heap)
  • 方法区(Method Area)
  • 本地方法栈(Native Method Stack)
  • 程序计数器(Program Counter)
  • 运行时常量池

Java虚拟机在执行Java程序的过程当中会把它管理的内存划分为若干个不一样的数据区域。这些区域都有各自的用途,根据Java虚拟机规范的规定,虚拟机所管理内存会被分为以下图所示的几个区域:程序员

 

2.HostSpot虚拟机对象建立

​ 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,而且检查这个符号引用表明的类是否已经被加载、解析和初始化过。若是没有,那必须先执行相应的类加载过程。在类加载检查经过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后,即可以彻底肯定,为对象分配空间的任务等同于把一块肯定大小的内存从java堆中划分出来。内存分配完成后,虚拟机将要分配到的内存空间都初始化为零值(不包括对象头),若是使用TLAb,这一工做过程也能够提早至TLAB分配时进行。这一步操做,保证了对象的实例字段在java代码中能够不赋是初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。最后在执行new指令后,会接着执行init方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算彻底产生出来。安全

2.1 java堆给对象分配内存的方式:

  • “指针碰撞(Bump the Pointer)”:假设java中的内存是绝对规整的,全部用过的内存都放在一边,空闲的内存放在另外一边,中间放着一个指针做为分界点的指示器,那全部分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离
  • “空闲列表(Free List)”:若是java堆中的内存并非规整的,已经使用的内存和空闲的内存相互交错,那就没有办法进行简单的指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是能够用的,在分配的时候,从列表中找到一块足够大的空间划分对象实例,并更新列表上的记录

2.2 解决对象内存分配的线程安全方案:

  • 一种是对分配内存空间的动做进行同步处理—实际上虚拟机采用CAS配上失败重试的方式保证更新操做的原子性;
  • 另外一种是把内存分配的动做按照线程划分在不一样的空间中进行,即每一个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲区(Thread Loacl Allocation Buffer,TLAB)。

ps:虚拟机是否使用TLAb,能够经过–XX:+/UseTLAB参数来设定网络

2.3 对象内存布局:

  • 对象头(Object Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

2.4 对象的访问定位:

句柄的方式:

说明:若是使用句柄访问的话,那么Java堆中将会划分出一起内存做为句柄池,reference中存储的就是对象的句柄
           地址,而句柄中包含了对象实例数据与类型数据各自的具体的地址信息。布局

 

直接指针的方式:

说明:若是使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的具体信息,而reference中
           存储的直接就是对象地址。学习

句方式柄与直接指针方式的对比: 

使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是很是广泛的行为)时只会改变句柄中的实例数据指针,而reference自己不须要修改;使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,因为对象的访问在Java中很是频繁,所以这类开销聚沙成塔后也是一项很是可观的执行成本。spa

注:就HotSpot而言,它是使用的直接指针的方式进行对象访问的。线程

2.5 异常实例

java.lang.OutOfMemoryError指针

java.lang.StackOverflowError对象

声明:本文是我的学习笔记,内容来自《深刻理解Java虚拟机·JVM高级特性与最佳实践》周志明 与网络文章

相关文章
相关标签/搜索