java 实例放在哪个区,常量放在哪个区
参考解析
题目来源:字节跳动
答案:村雨
实例放在哪个区
不要掉以轻心的一口回答在堆中!!!一般在java程序中,new的对象是分配在堆空间中的,但是实际的情况是,大部分的new对象会进入堆空间中,而并非是全部的对象,还有另外两个地方可以存储new的对象,我们称之为栈上分配以及TLAB
栈上分配
通过逃逸分析,我们发现,许多对象的生命周期会随着方法的调用开始而开始,方法的调用结束而结束,很多的对象的作用域都不会逃逸出方法外,对于此种对象,我们可以考虑使用栈上分配,而不是在堆中分配.
因为一旦分配在堆空间中,当方法调用结束,没有了引用指向该对象,该对象就需要被gc回收,而如果存在大量的这种情况,对gc来说反而是一种负担。
JVM提供了一种叫做栈上分配的概念,针对那些作用域不会逃逸出方法的对象,在分配内存时不在将对象分配在堆内存中,而是将对象属性打散后分配在栈(线程私有的,属于栈内存,标量替换)上,这样,随着方法的调用结束,栈空间的回收就会随着将栈上分配的打散后的对象回收掉,不再给gc增加额外的无用负担,从而提升应用程序整体的性能
那么问题来了,如果栈上分配失败了怎么办?
通过这张Java对象分配流程图可以发现,当栈上分配失败后,jvm会尝试进行TLAB分配
TLAB分配
TLAB:每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块”私有”内存中分配,当这部分区域用完之后,再分配新的”私有”内存,注意这个私有对于创建对象时是私有的,但是对于读取是共享的.
TLAB (Thread local allcation buffer ) 在“分配”这个动作上是线程独占的,至于在读取、垃圾回收等动作上都是线程共享的。在对象的创建时,首先尝试进行栈上分配,如果分配失败,会使用TLAB尝试分配,如果失败查看是否是大对象,如果是大对象直接进入老年代,否则进入新生代(Eden).
我们可以总结出: 创建大对象和创建多个小对象相比,多个小对象的效率更高
不知道大家有没有注意到,TLAB分配空间,每个线程在Java堆中预先分配一小块内存,他们在堆中去抢地盘的时候,也会出现并发问题,但是对于TLAB的同步控制和我们直接在堆中分配相比效率高了不少(不至于因为要分配一个对象而锁住整个堆了).
常量放在哪个区
java常量分配在常量池中
常量池
常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法和名称和描述符
虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。
对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。
在程序执行的时候,常量池会储存在Method Area,而不是堆中。