JVM学习

JVM由三部分组成:类加载子系统执行引擎运行时数据区。 1. 类加载子系统,可以根据指定的全限定名来载入类或接口。 2. 执行引擎,负责执行那些包含在被载入类的方法中的指令。 3. 当程序运行时,JVM需要内存来存储许多内容,例如:字节码、对象、参数、返回值、局部变量、运算的中间结果,等等,JVM会把这些东西都存储到运行时数据区中,以便于管理。而运行时数据区又可以分为方法区、堆、虚拟机栈、本地方法栈、程序计数器。

程序计数器

线程私有的,每个线程一份,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。


Java堆

线程共享的区域。主要用来保存对象实例,数组等,当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

Java堆Jdk1.7和1.8的区别
1.7有一个永久代,存储的是类信息、静态变量、常量、编译后的代码
1.8移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出

年轻代被划分为三部分,Eden区和两个大小严格相同的Survivor区,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到老年代区间
老年代主要保存生命周期长的对象,一般是一些老的对象
元空间保存的类信息、静态变量、常量、编译后的代码

Java虚拟机栈


每个线程运行时所需要的内存,称为虚拟机栈,先进后出
每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
面试题:1. 垃圾回收是否涉及栈内存?垃圾回收主要指就是堆内存,当栈帧弹栈以后,内存就会释放
面试题:2.栈内存分配越大越好吗?未必,默认的栈内存通常为1024k
栈帧过大会导致线程数变少,例如,机器总内存为512m,目前能活动的线程数则为512个,如果把栈内存改为2048k,那么能活动的栈帧就会减半
面试题:3. 方法内的局部变量是否线程安全?如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
面试题:4.栈内存溢出情况:栈帧过多导致栈内存溢出,典型问题:递归调用;栈帧过大导致栈内存溢出java.lang.StackOverflowError

堆栈的区别是什么

栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的的。堆会GC垃圾回收,而栈不会。
栈内存是线程私有的,而堆内存是线程共有的。
两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常。
     栈空间不足:java.lang.StackOverFlowError。
     堆空间不足:java.lang.OutOfMemoryError。

方法区

方法区(Method Area)是各个线程共享的内存区域。主要存储类的信息、运行时常量池。
虚拟机启动的时候创建,关闭虚拟机时释放元空间内存。如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspace

常量池

可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息。

运行时常量池:常量池是 *.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

直接内存

直接内存:并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存,常见于 NIO 操作时,用于数据缓冲区,它分配回收成本较高,但读写性能高

类加载器

面试题:什么是类加载器,类加载器有哪些?
JVM只会运行二进制文件,类加载器的作用就是将字节码文件加载到JVM中,从而让Java程序能够启动起来。
启动类加载器(BootStrap ClassLoader):加载JAVA_HOME/jre/lib目录下的库
扩展类加载器(ExtClassLoader):主要加载JAVA_HOME/jre/lib/ext目录中的类
应用类加载器(AppClassLoader):用于加载classPath下的类
自定义类加载器(CustomizeClassLoader):自定义类继承ClassLoader,实现自定义类加载规则。

面试题:什么是双亲委派模型?加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果该类委托上级没有被加载,子加载器尝试加载该类
面试题:JVM为什么采用双亲委派机制?
(1)通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保证唯一性。
(2)为了安全,保证类库API不会被修改

面试题:说一下类装载的执行过程?
类从加载到虚拟机中开始,直到卸载为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)
加载查找和导入class文件
验证保证加载类的准确性
准备为类变量分配内存并设置类变量初始值
解析把类中的符号引用转换为直接引用
初始化对类的静态变量,静态代码块执行初始化操作
使用JVM 开始从入口方法开始执行用户的程序代码
卸载当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象

对象什么时候可以被垃圾器回收

简单一句就是:如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收。
如果要定位什么是垃圾,有两种方式来确定,第一个是引用计数法,第二个是可达性分析算法

引用计数法:一个对象被引用了一次,在当前的对象头上递增一次引用次数,如果这个对象的引用次数为0,代表这个对象可回收
当对象间出现了循环引用的话,则引用计数法就会失效
可达性分析算法:现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾。

面试题:当一个对象设置为null,是否会被立即回收?
不会,设置为null只是栈中指向的引用为null,但是new出来的对象还在堆内存中。一个对象设置为null,并不会立即被回收,还需要进行finalize()方法。
当对象与GC root 无相连的引用链后,会被第一次标记,然后进行筛选,筛选条件为该对象是否有必要执行的finalize()方法。如果对象没有覆盖finalize()或者finalize()已经被虚拟机调用了,则以上两种情况都会被定位没有必要执行。
当对象判定为有必要执行finalize()方法时,对象会被置于F-Queue队列里,然后会有虚拟机自动建立的,调度优先级的Finalizer线程会去执行finalize()方法。finalize()方法是对象逃亡死亡最后一次机会,稍后收集器会对队列里的对象进行第二次小规模标记,二次标记后将会被回收。

面试题:JVM 垃圾回收算法有哪些?
标记清除算法
复制算法
标记整理算法
标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。
1.根据可达性分析算法得出的垃圾进行标记
2.对这些标记为可回收的内容进行垃圾回收
优点:标记和清除速度较快
缺点:碎片化较为严重,内存不连贯的
标记整理算法:优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。
复制算法:
优点:在垃圾对象多的情况下,效率较高清理后,内存无碎片
缺点:分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

面试题:说一下JVM中的分代回收
一、堆的区域划分
堆被分为了两份:新生代和老年代【1:2】
对于新生代,内部又被分为了三个区域。Eden区,幸存者区survivor(分成from和to)【8:1:1】


二、对象回收分代回收策略
新创建的对象,都会先分配到Eden区。当Eden内存不足,标记Eden与 from(现阶段没有)的存活对象,将存活对象采用复制算法复制到to中,复制完毕后,Eden和 from 内存都得到释放。经过一段时间后,Eden的内存又出现不足,标记Eden、to区存活的对象,将其复制到from区。当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会提前晋升)
面试题:MinorGC、 Mixed GC 、 FullGC的区别是什么
MinorGC【young GC】发生在新生代的垃圾回收,暂停时间短STW(Stop-The-World:暂停所有应用程序线程,等待垃圾回收的完成)
Mixed GC 新生代 + 老年代部分区域的垃圾回收,G1 收集器特有
FullGC: 新生代 + 老年代完整垃圾回收,暂停时间长(STW),应尽力避免
面试题:说一下 JVM 有哪些垃圾回收器?
在jvm中,实现了多种垃圾收集器,包括:
串行垃圾收集器
并行垃圾收集器
CMS(并发)垃圾收集器
G1垃圾收集器

G1垃圾收集器

应用于新生代和老年代,在JDK9之后默认使用G1
划分成多个区域,每个区域都可以充当 Eden,survivor,old, humongous,其中 humongous 专为大对象准备
采用复制算法(优点:没有内存碎片)
响应时间与吞吐量兼顾
分成三个阶段:新生代回收、并发标记、混合收集
如果并发失败(即回收速度赶不上创建新对象速度),会触发 Full GC

JVM调优的参数都有哪些

对于JVM调优,主要就是调整年轻代、老年代、元空间的内存空间大小及使用的垃圾回收器类型。
https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html 
设置堆空间大小:设置堆的初始大小和最大大小,为了防止垃圾收集器在初始大小、最大大小之间收缩堆而产生额外的时间,通常把最大、初始大小设置为相同的值。

-Xms:设置堆的初始化大小
-Xmx:设置堆的最大大小
不指定单位默认为字节,指定单位,按照指定的单位设置   -Xms:1024m

面试题:堆空间设置多少合适?最大大小的默认值是物理内存的1/4,初始大小是物理内存的1/64
堆太小,可能会频繁的导致年轻代和老年代的垃圾回收,会产生stw,暂停用户线程
堆内存大肯定是好的,存在风险,假如发生了fullgc,它会扫描整个堆空间,暂停用户线程的时间长
设置参考推荐:尽量大,也要考察一下当前计算机其他程序的内存使用情况

虚拟机栈的设置
虚拟机栈的设置:每个线程默认会开启1M的内存,用于存放栈帧、调用参数、局部变量等,但一般256K就够用。通常减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。-Xss256k  

年轻代中Eden区和两个Survivor区的大小比例
设置年轻代中Eden区和两个Survivor区的大小比例。该值如果不设置,则默认比例为8:1:1。通过增大Eden区的大小,来减少YGC发生的次数,但有时我们发现,虽然次数减少了,但Eden区满的时候,由于占用的空间较大,导致释放缓慢,此时STW的时间较长,因此需要按照程序情况去调优。年轻代晋升老年代阈值 默认为15,取值范围0-15设置垃圾回收收集器  -XX:+UseG1GC

JVM 调优的工具

命令工具
jps          进程状态信息
jstack     查看java进程内线程的堆栈信息
jmap      查看堆转信息
jhat       堆转储快照分析工具
jstat      JVM统计监测工具
可视化工具
jconsole      用于对jvm的内存,线程,类 的监控
VisualVM    能够监控线程,内存情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值