- 运行时数据区
- 1.说一下JVM的内存模型吧,有哪些区?分别干什么的?
- 2.为什么要用程序计数器存储字节码指令地址?
- 3.程序计数器为什么要设置为线程私有?
- 4.CPU是怎样切换线程的呢?
- 5.栈和堆的区别?
- 6.方法中定义的局部变量是线程安全的吗?堆是分配对象存储的唯一选择吗?
- 7.为什么要有新生代和老年代?
- 8.为什么要有Survivor区?
- 9.为什么还需要两个Survivor区?
- 10.什么是TLAB(Thread Local Allocation Buffer)?为什么有TLAB?
- 11.JVM内存的参数设置
- 12.jvm内存模型,java8做了什么修改?
- 13.为什么永久代会改为元空间?为什么jdk7之后把StringTable字符串常量池放到堆空间中?
- 14.方法区中有垃圾回收吗?
因为CPU需要不断的切换线程,这时候切换回来以后,就得知道接着从哪开始执行。所有需要用程序计数器记录下一条指令的指令地址
JVM的字节码解释器就是通过改变程序计数器的值来确定下一条应该执行什么样的字节码指令
因为CPU需要不断的切换线程,如果程序计数器设置为线程共享的,那么程序计数器记录当前线程的指令地址就会覆盖上一个线程的下一条指令的指令地址。如果再切换回上一个线程,因为指令地址被覆盖就不知道接着从哪开始执行了。所以程序计数器必须设置为线程私有,为每一个线程分配一个程序计数器,这样就不会相互干扰了。
4.CPU是怎样切换线程的呢?使用CPU时间片进行切换的,CPU会给每个线程分配一个时间段,这段时间叫做时间片。
多线程从宏观上来看它是多个线程一起运行的。
但是微观角度,CPU同一时刻只能运行一个线程,需要不断的切换线程。为了保证每个线程公平处理,就是给每个线程分配一个时间片,时间到了就别运行了换另一个线程运行。
栈是运行时的单位,解决程序的运行问题。 堆是存储的单位,解决数据存储的问题。
栈是每个线程私有的,堆是线程共享的一块内存区域。
JVM对栈操作只有进栈和出栈操作,因此栈中是没有垃圾回收;而堆是垃圾回收的主要区域。
如果经过逃逸分析,一个对象如果没有逃逸出方法的话,那么就可能被优化成栈上分配
如果对象是方法内部生成,方法内部消亡的就认为没有发生逃逸。当一个对象在方法内部生成后,它被其它线程所调用,则认为发生了逃逸。
局部变量是不是线程安全要看具体情况具体分析,主要是看这个局部变量引用的对象有没有被其它线程调用。
局部变量引用的对象是在内部生成,内部消亡就是线程安全的
形参的局部变量线程不安全
作为返回值返回也是线程不安全
特殊情况是线程安全的
分代的唯一理由就是优化GC性能。如果没有分代,那所有对象都放在一块。GC的时候如果要找到哪些对象能被回收,就需要对堆中所有区域进行扫描。然而实际上很多对象都是"朝生夕死"的,如果我们把这些寿命短的对象放在一个区,gc的时候优先清理这块区域,就样就会很快腾出空间出来,提高效率。
8.为什么要有Survivor区?如果没有Survivor区,每次Eden空间满了发生了minor gc的时候,存活的对象就会放到老年区,老年区满了就会触发Full GC,Full GC是非常耗时的,如果把Eden空间存活的对象放到Survivor区,等对象反复清理几遍都没清理掉再放到老年区,这样老年区的压力就会小很多。Survivor相当于一个筛子,筛掉生命周期短的,将生命周期长的放到老年代区,减少老年代发生full gc的次数。
9.为什么还需要两个Survivor区?待补充…为了解决内存碎片化
10.什么是TLAB(Thread Local Allocation Buffer)?为什么有TLAB?因为堆区是线程共享区域,如果多个线程先后把对象的引用指向同一个内存区域就会造成非线程安全问题,为了解决这个并发问题,对象的内存分配过程就必须进行同步控制,也必定会影响内存的分配效率。由于jvm创建实例是非常高频的。所以JVM在伊甸园为每个线程分配了一个私有缓存区域,如果需要分配内存,就优先自己的空间上分配,这样就不存在竞争情况,可以大大提高分配效率。
11.JVM内存的参数设置-Xss 设置栈空间大小
-Xms:初始堆空间大小
-Xmx:最大堆空间大小
-Xmn:设置新生代的大小
-XX:NewRatio=2 配置新生代和老年代的比例
-XX:SurvivorRatio=8 配置伊甸园区和幸运者区的比例 相当于8:1:1
-XX:+PrintGCDetails 输出详细的GC处理日志
-XX:+PrintGC 打印gc简要信息
-XX:MaxTenuringThreshold 设置新生代垃圾的最大年龄
-XX:HandlePromotionFailure 是否设置空间分配担保
JDK6之后代码中不会再使用此参数,在minor gc之前规则变为只要老年代的连续空间大于新生代对象总大小或者历代晋升到老年代对象的平均大小就会进行Minor,否则将进行Full GC
-XX:+PrintFlagsInitial 查看所有参数默认初始值
-XX:+PrintFlagsFinal 查看所有参数的最终值
-XX:metaspaceSize 元空间初始可分配空间(默认约为21M)
-XX:MaxmetaspaceSize 元空间的最大可分配空间(默认-1没有限制)
这里指的是静态变量而不是静态变量的引用,静态变量引用的对象实体始终都是存在堆空间。
jdk7及其以后版本,HotSpot虚拟机选择把静态变量与引用的对象存放在一起,存储于java堆之中。
永久代设置空间大小是很难确定的,如果动态加载的类过多,容易产生Perm区的OOM。而元空间跟永久代最大的区别是,元空间并不在虚拟机设置的内存中,而是使用本地内存。因为默认情况下,元空间大小仅受本地内存限制。
因为方法区回收效率很低,只有当方法区空间不足才会触发full gc,这就导致字符串常量池回收效率不高,而我们开发中会有大量的字符串被创建,放到堆空间能及时回收内存。
方法区中的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。但是这个区域回收效果不太理想,尤其是类型信息的回收,条件非常苛刻。



