JVM系列(2)-对象和OOM异常

JVM系列(2)-对象和OOM异常


上一讲中,我们了解了JVM的内部结构,更准确的讲应该是JVM运行时数据区,知道了对象的实例是在堆中分配的。那到底是怎么创建的,都有哪些信息需要创建,我们今天就先讲下这个,以Hotspot实现为例。

JVM系列(2)-对象和OOM异常


问题1:对象是怎么创建的?


我们创建对象的时候,会使用new关键字,像这样:Object obj = new Object();虚拟机在遇到new这个关键字的时候,会先检查这个类有没有被加载。如果没有,则需要先加载这个类;如果已经加载,则为对象分配内存。至于需要划分多大的堆内存,类加载完成后,这个大小就确定了,这也是为什么要在分配内存前,先确保类已加载;因为只有加载后,才知道需要分配多大内存。


需要分配的内存大小确定了,那就划分一块内存呗,但这里又有几个问题。

1)JVM的堆内存是规整的,还是凌乱的?

规整的就是,已经使用过的堆内存和没有使用的堆内存有一个明确的分界线。像下面这样,要分配新内存时,直接在空闲区划拉一块儿。专业的叫法是“指针碰撞”。

JVM系列(2)-对象和OOM异常



如果是不规整的,像下面这样,就需要一个列表来记录,记录哪些是空闲的。这种方式称作“空闲列表”。

JVM系列(2)-对象和OOM异常

2)并发情况下,在线程1尝试分配对象A内存时,还没来得及分配,就让出了CPU和时间片,此时,线程2执行,尝试分配对象B内存,使用的是相同的指针(分配的起始地址)。这就有问题了。

解决方法

i>采用同步方法处理分配内存的动作;

ii>TLAB(Thread Local Allocation Buffer),对每个线程,预先在堆中分配一块儿内存,每个线程,首先在自个儿的这个区间分配内存,在消耗完之后,再做同步处理。


-XX:+/-UseTLAB控制JVM是否采用TLAB这种方式。


对象创建后,在内存中都记录了哪些信息?我们继续往下看。


问题2:对象的内存布局


JVM系列(2)-对象和OOM异常

对象内存区域分3个,对象头 + 实例数据 + 对齐填充。

对象头又包括运行时数据 + 类型指针。


有没有想过对象的类型信息是存在哪儿的?如下图,类型信息存在方法区。

JVM系列(2)-对象和OOM异常

问题3:OutOfMemoryError异常


JVM系列(2)-对象和OOM异常

JVM运行时数据区包括方法区 + 堆 + 虚拟机栈 + 本地方法栈 + 程序计数器。除了程序计数器不会抛出异常外,其他的区域都有可能抛出OOM Error。

1) 堆

JVM通过-Xmx和-Xms参数控制堆大小

2)栈

JVM通过-Xoss和-Xss参数控制栈的大小

3)方法区

JVM通过-XX:PermSize和-XX:MaxPermSize参数控制大小


这块儿简单讲了下,明天讲下垃圾收集和内存分配。

JVM系列(2)-对象和OOM异常