对象的创建与内存分配
2018-01-18 17:22
225 查看
创建对象
当JVM收到一个
new指令时,会检查指令中的参数在常量池是否有这个符号的引用,还会检查该类是否已经被加载过了,如果没有的话则要进行一次类加载。
接着就是分配内存了,通常有两种方式:
指针碰撞
空闲列表
使用指针碰撞的前提是堆内存是完全工整的,用过的内存和没用的内存各在一边每次分配的时候只需要将指针向空闲内存一方移动一段和内存大小相等区域即可。
当堆中已经使用的内存和未使用的内存互相交错时,指针碰撞的方式就行不通了,这时就需要采用空闲列表的方式。虚拟机会维护一个空闲的列表,用于记录哪些内存是可以进行分配的,分配时直接从可用内存中直接分配即可。
堆中的内存是否工整是有垃圾收集器来决定的,如果带有压缩功能的垃圾收集器就是采用指针碰撞的方式来进行内存分配的。
分配内存时也会出现并发问题:
这样可以在创建对象的时候使用
CAS这样的乐观锁来保证。
也可以将内存分配安排在每个线程独有的空间进行,每个线程首先在堆内存中分配一小块内存,称为本地分配缓存(
TLAB : Thread Local Allocation Buffer)。
分配内存时,只需要在自己的分配缓存中分配即可,由于这个内存区域是线程私有的,所以不会出现并发问题。
可以使用
-XX:+/-UseTLAB参数来设定 JVM 是否开启
TLAB。
内存分配之后需要对该对象进行设置,如对象头。对象头的一些应用可以查看 Synchronize 关键字原理。
对象访问
一个对象被创建之后自然是为了使用,在 Java 中是通过栈来引用堆内存中的对象来进行操作的。对于我们常用的
HotSpot虚拟机来说,这样引用关系是通过直接指针来关联的。
如图:
这样的好处就是:在 Java 里进行频繁的对象访问可以提升访问速度(相对于使用句柄池来说)。
内存分配
Eden 区分配
简单的来说对象都是在堆内存中分配的,往细一点看则是优先在Eden区分配。
这里就涉及到堆内存的划分了,为了方便垃圾回收,JVM 将对内存分为新生代和老年代。
而新生代中又会划分为
Eden区,
from Survivor、to Survivor区。
其中
Eden和
Survivor区的比例默认是
8:1:1,当然也支持参数调整
-XX:SurvivorRatio=8。
当在
Eden区分配内存不足时,则会发生
minorGC,由于
Java对象多数是朝生夕灭的特性,所以
minorGC通常会比较频繁,效率也比较高。
当发生
minorGC时,JVM 会根据复制算法将存活的对象拷贝到另一个未使用的
Survivor区,如果
Survivor区内存不足时,则会使用分配担保策略将对象移动到老年代中。
谈到
minorGC时,就不得不提到
fullGC(majorGC),这是指发生在老年代的
GC,不论是效率还是速度都比
minorGC慢的多,回收时还会发生
stop the world使程序发生停顿,所以应当尽量避免发生
fullGC。
老年代分配
也有一些情况会导致对象直接在老年代分配,比如当分配一个大对象时(大的数组,很长的字符串),由于Eden区没有足够大的连续空间来分配时,会导致提前触发一次
GC,所以尽量别频繁的创建大对象。
因此
JVM会根据一个阈值来判断大于该阈值对象直接分配到老年代,这样可以避免在新生代频繁的发生
GC。
对于一些在新生代的老对象
JVM也会根据某种机制移动到老年代中。
JVM 是根据记录对象年龄的方式来判断该对象是否应该移动到老年代,根据新生代的复制算法,当一个对象被移动到
Survivor区之后 JVM 就给该对象的年龄记为1,每当熬过一次
minorGC后对象的年龄就 +1 ,直到达到阈值(默认为15)就移动到老年代中。
可以使用
-XX:MaxTenuringThreshold=15来配置这个阈值。
总结
虽说这些内容略显枯燥,但当应用发生不正常的GC时,可以方便更快的定位问题。
号外
最近在总结一些 Java 相关的知识点,感兴趣的朋友可以一起维护。地址: https://github.com/crossoverJie/Java-Interview
相关文章推荐
- 创建String对象过程的内存分配
- 准备写:创建对象时的内存分配
- 关于创建String对象过程的内存分配
- Java创建子类对象时的内存分配
- 对象的创建与内存分配
- 关于创建String对象过程的内存分配
- Java虚拟机对象创建及其内存分配
- 创建string 对象的内存分配
- [C++基础]关于对象的创建及内存分配
- 创建string对象过程的内存分配
- 关于创建String对象过程的内存分配
- 解析Java中创建String对象过程中的内存分配
- \t\t深入了解javascript的面向对象特性 类和对象的创建 实例化
- C++类的定义和对象的创建 在栈上创建 在堆上创建
- JS中用new 创建对象及在构造函数中return的使用
- Web页面设计时提示"创建控件出错,未将对象引用设置到对象的实例”的错误解决办法
- 避免在循环体中创建对象
- 对象创建,对象方法
- Javascript创建对象
- golang使用反射创建对象