2.1java代码到底是如何运行起来的
见图
2.2画一下整个JVM运行原理图
见图
2.3请介绍一下JVM的内存结构划分
见图
2.4JVM哪些区域是线程私有的,哪些区域是线程共享的?
1、堆、元空间(方法区)是线程共享的;
2、其他区域是线程私有的;
2.6 JVM运行时数据区程序计数器的特点及作用?
1、程序计数器是一块较小的内存空间,几乎可以忽略;
2、是当前线程所执行的字节码的行号指示器;
3、Java多线程执行时,每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响;
4、该区域是“线程私有”的内存,每个线程独立存储;
5、该区域不存在OutOfMemoryError;
6、无GC回收;
2.7 JVM运行时数据区虚拟机栈的特点及作用?
1、线程私有;
2、方法执行会创建栈帧,存储局部变量表等信息;
3、方法执行入虚拟机栈,方法执行完出虚拟机栈;(先进后出)
4、栈深度大于虚拟机所允许StackOverflowError;(一般出现在递归调用的时候)
5、栈需扩展而无法申请空间OutOfMemoryError(比较少见); hotspot虚拟机没有;
6、栈里面运行方法,存放方法的局部变量名,变量名所指向的值(常量值、对象值等)都存放到堆上的;
7、栈一般都不设置大小,栈所占的空间其实很小,可以通过-Xss1M进行设置,如果不设置默认为1M;
8、随线程而生,随线程而灭;
9、该区域不会有GC回收;
2.8 JVM运行时数据区本地方法栈的特点及作用?
1、与虚拟机栈基本类似;
2、区别在于本地方法栈为Native方法服务;
3、HotSpot虚拟机将虚拟机栈和本地方法栈合并;
4、有StackOverflowError和OutOfMemoryError (比较少见);
5、随线程而生,随线程而灭;
6、GC不会回收该区域;
2.9 JVM运行时数据区Java堆的特点及作用?
1、线程共享的一块区域;
2、虚拟机启动时创建;
3、虚拟机所管理的内存中最大的一块区域;
4、存放所有实例对象和数组;
5、GC垃圾收集器的主要管理区域;
6、可分为新生代、老年代;
见图
7、新生代更细化可分为Eden、From Survivor、To Survivor,Eden:Survivor = 8:1:1
8、可通过-Xmx、-Xms调节堆大小;
9、无法再扩展java.lang.OutOfMemoryError: Java heap space
10、如果从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),以提升对象分配时的效率;
2.10 JVM中对象如何在堆内存分配?
1、指针碰撞(Bump The Pointer) :内存规整的情况下;
2、空闲列表(Free List) :内存不规整的情况下;
选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(Compact)的能力决定;
因此,当使用Serial、ParNew等带压缩整理过程的收集器时,系统采用的分配算法是指针碰撞,既简单又高效;
而当使用CMS这种基于清除(Sweep)算法的收集器时,理论上就只能采用较为复杂的空闲列表来分配内存;
3、本地线程分配缓冲r(Thread Local Allocation Buffer,TLAB)︰对象创建在虚拟机中频繁发生,即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现
正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况;
那么解决方案有两种:
(1)同步锁定,JVM是采用CAS配上失败重试的方式保证更新操作的原子性;
(2)线程隔离,把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),
哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定,虚拟机是否使用TLAB,可以通过-XX: +/-UseTLAB参数来设定;如果是0的话就自动分配
2.11 JVM堆内存中的对象布局?
在HotSpot虚拟机中,一个对象的存储结构分为3块区域:对象头(Header)、实例数据
(Instance Data)和对齐填充(Padding);
对象头(Header):包含两部分,第一部分用于存储对象自身的运行时数据,
如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,32位虚拟机占32 bit,
64位虚拟机占64 bit,官方称为‘Mark Word';
第二部分是类型指针,即对象指向它的类的元数据指针,虚拟机通过这个指针确定这个对象是
哪个类的实例,另外,如果是Java数组,对象头中还必须有一块用于记录数组长度的数据,
因为普通对象可以通过Java 对象元数据确定大小,而数组对象不可以;
实例数据(Instance Data):程序代码中所定义的各种成员变量类型的字段内容(包含父类继承
下来的和子类中定义的);
对齐填充(Padding):不是必然需要,主要是占位,保证对象大小是某个字节的整数倍,
HotSpot虚拟机,任何对象的大小都是8字节的整数倍;
2.12JVM什么情况下会发生堆内存溢出?
Java堆中用于储存对象,只要不断地创建对象,并且保持GC Roots到对象之间有可达路径来避免垃圾回收机制清理这些对象,那么随着对象数量的增加,总容量达到最大堆的容量限制后就会产生内存溢出;
MAT工具分析xxx.hprof 文件,排查溢出的原因;
-Xms3072M
-Xmx3072M
-XX:-UseCompressedclassPointers
-XX:Metaspacesize=256M
-XX:MaxMetaspacesize=256M
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=5
-XX:PretenuresizeThreshold=1M
-XX:+PrintGCDetails
-XX:+PrintGCDatestamps
-Xloggc:d: /dev/gc.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:/dev/heapdump.hprof
2.13 JVM如何判断对象可以被回收?
在JVM堆里面存放着所有的Java对象,垃圾收集器在对堆进行回收前,首先要确定这些对象之中哪些还“存活”着,哪些已经“死去”;
Java通过可达性分析(Reachability Analysis)算法来判定对象是否存活的;
该算法的基本思路:通过一系列称为“GC Roots”的根对象作为起始节点,从这些节点开始,
根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连(也称为不可达),则证明此对象是不可能再被使用的对象,就可以被垃圾回收器回收;
见图
对象object 5、object 6、object7虽然互有关联,但是它们到GKOOts定个可H的,凶此
它们将会被判定为可回收的对象;
哪些对象可以作为GC Roots呢?
1、在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等所引用的对象;
2、方法区/元空间中的类静态属性引用的对象;
3、方法区/元空间中的常量引用的对象;
4、在本地方法栈中JNI(即通常所说的Native方法)引用的对象;
5、Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器;
6、所有被同步锁(synchronized关键字)持有的对象;
7、反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等;
8、其他可能临时性加入的对象;