JVM系列(2)-对象和OOM异常
上一讲中,我们了解了JVM的内部结构,更准确的讲应该是JVM运行时数据区,知道了对象的实例是在堆中分配的。那到底是怎么创建的,都有哪些信息需要创建,我们今天就先讲下这个,以Hotspot实现为例。
问题1:对象是怎么创建的?
我们创建对象的时候,会使用new关键字,像这样:Object obj = new Object();虚拟机在遇到new这个关键字的时候,会先检查这个类有没有被加载。如果没有,则需要先加载这个类;如果已经加载,则为对象分配内存。至于需要划分多大的堆内存,类加载完成后,这个大小就确定了,这也是为什么要在分配内存前,先确保类已加载;因为只有加载后,才知道需要分配多大内存。
需要分配的内存大小确定了,那就划分一块内存呗,但这里又有几个问题。
1)JVM的堆内存是规整的,还是凌乱的?
规整的就是,已经使用过的堆内存和没有使用的堆内存有一个明确的分界线。像下面这样,要分配新内存时,直接在空闲区划拉一块儿。专业的叫法是“指针碰撞”。
如果是不规整的,像下面这样,就需要一个列表来记录,记录哪些是空闲的。这种方式称作“空闲列表”。
2)并发情况下,在线程1尝试分配对象A内存时,还没来得及分配,就让出了CPU和时间片,此时,线程2执行,尝试分配对象B内存,使用的是相同的指针(分配的起始地址)。这就有问题了。
解决方法
i>采用同步方法处理分配内存的动作;
ii>TLAB(Thread Local Allocation Buffer),对每个线程,预先在堆中分配一块儿内存,每个线程,首先在自个儿的这个区间分配内存,在消耗完之后,再做同步处理。
-XX:+/-UseTLAB控制JVM是否采用TLAB这种方式。
对象创建后,在内存中都记录了哪些信息?我们继续往下看。
问题2:对象的内存布局
对象内存区域分3个,对象头 + 实例数据 + 对齐填充。
对象头又包括运行时数据 + 类型指针。
有没有想过对象的类型信息是存在哪儿的?如下图,类型信息存在方法区。
问题3:OutOfMemoryError异常
JVM运行时数据区包括方法区 + 堆 + 虚拟机栈 + 本地方法栈 + 程序计数器。除了程序计数器不会抛出异常外,其他的区域都有可能抛出OOM Error。
1) 堆
JVM通过-Xmx和-Xms参数控制堆大小
2)栈
JVM通过-Xoss和-Xss参数控制栈的大小
3)方法区
JVM通过-XX:PermSize和-XX:MaxPermSize参数控制大小
这块儿简单讲了下,明天讲下垃圾收集和内存分配。