《深入理解Java虚拟机》笔记--Java内存与内存溢出

2019-10-20  本文已影响0人  Android_17

一.JAVA内存

1.运行时数据区域

线程隔离区:程序计数器,虚拟机栈,本地方法栈
线程共享区:方法区,堆

1.1程序计数器

多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各个线程直接计数器互不影响,独立存储。

1.2虚拟机栈

粗糙的将Java内存分为堆和栈,那这里的“栈”指的就说虚拟机栈,或者说是虚拟机栈中的局部变量表部分

1.3本地方法栈

与虚拟机栈的区别是,为虚拟机所用到的Native方法服务

1.4堆

是虚拟机所管理的内存中最大的一块,是线程共享的,在虚拟机启动时创建。

1.5 方法区

线程共享,用于存储意已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

1.5.1运行时常量

是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将在,类加载后进入方法区的运行时常量池中,存放

2.HotSpot虚拟机对象

2.1对象的创建

1.new关键字开始
2.类加载检查
3.分配内存
4.内存空间初始化为零值(不包括对象头)
5.设置
6.执行Init

类加载检查

分配内存

初始化为零值

设置

执行Init

2.2对象的内存布局

在HotSpot虚拟机中,对象在内存中的存储布局可以分为3块区域:对象头、实例数据、对齐填充

对象头

对象头包括两部分信息

  1. 用于存储对象自身的运行时数据:哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等
  2. 存储类型指针,即指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。(查找元数据并不一定要经过对象本身 )

注:如果是Java数组,那么对象头中还必须要有一块区域用于记录数组长度的数据,因为Java虚拟机可以通过普通Java对象的元数据信息来确定Java对象的大小,但是从数组的元数据中无法确定数组的大小

实例数据

实例数据是对象真正存储的有效信息,也就说程序代码中所定义的各种类型的字段内容,无论是从父类继承下来的,还是本身定义的,都要记录下来

对齐填充

对齐填充部必然存在,因为对象的大小必须说8字节的倍数,当对象实例数据部分没有对齐时,就要通过对齐填充来补齐

2.3对象的访问定位

Java程序需要通过栈上的reference数据来操作上的具体对象。由于reference类型值规定来一个指向对象的引用,而具体引用的方式目前有两种:使用句柄、直接指针
tip:reference类型存储在虚拟机栈的栈帧中的局部变量表中

二.outOfMemoryError异常

虚拟机规范中描述:除了程序计数器外,其他的运行时区域都有发生OOM的可能

1.Java堆溢出

Java堆用于存储对象实例,只要不要地创建对象,并且保证GC Root到对象之间有可达路径来避免垃圾回收机制来清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常

2.虚拟机栈和本地方法栈溢出

由于HotSpot虚拟机并不区分虚拟机栈和本地方法栈(只是分别执行Java方法和native方法),因此虽然设置本地方法栈大小存在,但实际上上无效的,栈容量由虚拟机栈容量确定

3.方法区和运行时常量池溢出

运行时常量池上方法区的一部分

4.本地直接内存溢出

上一篇 下一篇

猜你喜欢

热点阅读