常见面试题(二)JVM与内存管理

1、什么情况下会产生栈内存溢出异常?

如果线程请求分配的栈容量超过java虚拟机栈允许的最大容量的时候,java虚拟机将抛出一个StackOverFlowError异常,可通过-Xss参数适当调整大小。

2、JVM内存结构,以及Eden和Survivor比例

1)堆内存:最大的一块内存区域,线程共享,主要用于存放对象实例,分为一个Eden和两个Survivor,比例为8:1:1

2)方法区:线程共享区域,存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据;

3)程序计数器:线程私有,一块较小的空间,当前线程执行的字节码的行号指示器,一般不会出现内存溢出;

4)java虚拟机栈:线程私有,与线程生命周期相同,存储局部变量表、操作栈、动态链接、方法出口等信息;

5)本地方法栈:为虚拟机用到的Native方法服务的

3、JVM内存为什么要分成新生代,老年代,持久代。

方便对内存中对象的管理,和进行垃圾回收

4、新生代中为什么要分为Eden和Survivor

Survivor存在的意义减少被送到老年代的对象,减少Full GC的次数,经过Survivor预筛选,只有经过16次 minor GC还存活的对象才会进入老年代。两个Survivor区是为了始终保证一个Survivor区是空的,另一个非空的无碎片

5、JVM中一次完整的GC流程是怎样的?

在Eden区创建新对象,如果Eden区空间不足,则触发一次minor GC对Eden区的不可用对象进行回收,依然存活的对象则进入其中一个Survivor S0区,另一个Survivor S1作为S0的备份空间,当S0空间满时,此时发生的minor GC会将S0和Eden中依然存活的对象移到S1,然后将其他不可用对象进行清除,这样在S0和S1之间来回复制,经过16次(默认)依然存活的对象则会进入年老代

6、对象如何晋升到老年代?

对象优先在新生代的 eden 区分配内存,但是也并不绝对,下面几种情况对象会晋升到老年代

  • Eden区满时,进行Minor GC,当Eden和一个Survivor区中依然存活的对象无法放入到Survivor中,则通过分配担保机制提前转移到老年代中。
  • 大对象直接进入老年代。比如很长的字符串,或者很大的数组等。
  • 长期存活的对象进入老年代。在堆中分配内存的对象,其内存布局的对象头中(Header)包含了 GC 分代年龄标记信息。如果对象在 eden 区出生,那么它的 GC 分代年龄会初始值为 1,每熬过一次 Minor GC 而不被回收,这个值就会增加 1 岁。当它的年龄到达一定的数值时(jdk1.7 默认是 15 岁),就会晋升到老年代中。
  • 动态对象年龄判定。当 Survivor 空间中相同年龄所有对象的大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,而不需要达到默认的分代年龄。

 

7、垃圾对象判断方法

1)引用计数:目前垃圾回收没有采用引用计数算法,原因是在对象互相引用的情况下,无法判定两者是否为垃圾对象。

2)根搜索法:通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的;

可以作为GCRoots的对象包括下面几种:

. 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

. 方法区中的类静态属性引用的对象。

. 方法区中常量引用的对象。

. 本地方法栈中JNI(Native方法)引用的对象。

8、垃圾回收算法

1)标记-清除:jvm会扫描所有的对象实例,通过根搜索算法,将活跃对象进行标记,jvm再一次扫描所有对象,将未标记的对象进行清除,只有清除动作,不作任何的处理,这样导致的结果会存在很多的内存碎片。

2)复制:jvm扫描所有对象,通过根搜索算法标记被引用的对象,之后会申请新的内存空间,将标记的对象复制到新的内存空间里,存活的对象复制完,会清空原来的内存空间,将新的内存最为jvm的对象存储空间。这样虽然解决了内存内存碎片问题,但是如果对象很多,重新申请新的内存空间会很大,在内存不足的场景下,会对jvm运行造成很大的影响;

3)标记-整理:标记整理实际上是在标记清除算法上的优化,执行完标记清除全过程之后,再一次对内存进行整理,将所有存活对象统一向一端移动,这样解决了内存碎片问题。

4)分代回收:目前jvm常用回收算法就是分代回收,年轻代回收率高,存活对象少,年轻代以复制算法为主,老年代以标记整理算法为主

9、垃圾回收器

年轻代垃圾回收器:

1)Serial:可以和Serial Old、CMS组合使用、采用复制算法、单线程串行回收、会STW、client模式年轻代默认

2)ParNew:可以和Serial Old、CMS组合使用、采用复制算法、多线程回收、会STW

3)Paralle Scavange:和Serial Old、Parallel组合使用、复制算法、多线程回收、会STW、server模式年轻代默认

年老代垃圾回收器:

1)Serial Old:和所有的年轻代收集器组合使用、”标记-整理“算法、单线程、STW

2)ParalleOld:只能和Parallel Scavenge组合使用、”标记-整理“、多线程、STW,Parallel Scavenge+ParalleOld提高吞吐量

3)CMS:可以和Serial、ParNew组合使用、”标记-清除“、并发,部分阶段STW

1、初始标记:STW,仅仅标记一下GC Roots能直接关联到的对象,速度快

2、并发标记:进行GC Roots Tracing,时间长,不发生用户进程停顿

3、重复标记:STW,修正并发标记期间因用户程序继续运行导致标记变动的那一部分对象的标记记录

4、并发清除:清除的同时用户进程会导致新的垃圾,时间长,不发生用户进程停顿、

适用于响应时间要求高,缺点为:对CPU资源敏感、产生碎片

4)G1垃圾收集器也是以关注延迟为目标、服务器端应用的垃圾收集器,被HotSpot团队寄予取代CMS的使命,也是一个非常具有调优潜力的垃圾收集器。虽然G1也有类似CMS的收集动作:初始标记、并发标记、重新标记、清除、转移回收,并且也以一个串行收集器做担保机制,但单纯地以类似前三种的过程描述显得并不是很妥当。事实上,G1收集与以上三组收集器有很大不同:

 

G1的设计原则是"首先收集尽可能多的垃圾(Garbage First)"。因此,G1并不会等内存耗尽(串行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;

G1采用内存分区(Region)的思路,将内存划分为一个个相等大小的内存分区,回收时则以分区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位进行操作,因此G1天然就是一种压缩方案(局部压缩);

G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换;

G1的收集都是STW的,但年轻代和老年代的收集界限比较模糊,采用了混合(mixed)收集的方式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。

 

7、JVM主要参数

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD))

-server :设置JVM使用server模式。64位JDK默认启动该模式

-agentlib:libname[=options] :用于加载本地的lib

-agentlib:hprof :用于获取JVM的运行情况

-agentpath:pathnamep[=options] :加载制定路径的本地库

-Dproperty=value :设置系统属性名/值对

-jar :制定以jar包的形式执行一个应用程序

-javaagent:jarpath[=options] :实现premain方法在main方法前执行可以利用该方式玩一个JVM层面的hook很有意思的东西

-verbose:jni :输出native方法的调用情况玩JNI必备技能

例如:

-agentlib:jprofilerti=port=8849 -Xbootclasspath/a:/usr/local/jprofiler5/bin/agent.jar

java堆栈大小设置相关

-Xms :设置Java堆栈的初始化大小

-Xmx :设置最大的java堆大小

-Xmn :设置Young区大小

-Xss :设置java线程堆栈大小

-XX:PermSize and MaxPermSize :设置持久带的大小

-XX:NewRatio :设置年轻代和老年代的比值

-XX:NewSize :设置年轻代的大小

-XX:SurvivorRation=n :设置年轻代中E去与俩个S去的比值

打印垃圾回收信息及设置垃圾回收器

-verbose:gc :记录GC运行以及运行时间,一般用来查看GC是否有瓶颈

-XX:+PrintGCDetails :记录GC运行时的详细数据信息,包括新生占用的内存大小及消耗时间

-XX:-PrintGCTimeStamps :打印收集的时间戳

-XX:+UseParallelGC :使用并行垃圾收集器

-XX:ParallelGCThreads=N,设置并行垃圾回收的线程数,此值可以设置与机器处理机数量一致(有建议core+3/4);

-XX:-UseConcMarkSweepGC :使用并发标志扫描收集器

-XX:-UseSerialGC :使用串行垃圾收集器

-Xloggc:filename :设置GC记录的文件

-XX:+UseGCLogFileRotation :启用GC日志文件的自动转储

-XX:GCLogFileSize=1M :控制GC日志文件的大小

-XX:+UseAdaptiveSizePolicy 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。

-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用输出形式:Application time: 0.5291524 seconds

-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用,输出形式:Total time for which application threads were stopped: 0.0468229 seconds

-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息  

调试参数

-Xdebug

-Xnoagent

-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

-XX:HeapDumpPath=./java_pid.hprof :Path to directory or file name for heap dump.

-XX:-PrintConcurrentLocks :Print java.util.concurrent locks in Ctrl-Break thread dump.

-XX:-PrintCommandLineFlags :Print flags that appeared on the command line.

关于性能

-Xprof

-Xrunhprof

类加载和跟踪类加载和卸载的信息

Xbootclasspath :指定需要加载,但不想通过校验类路径。

JVM会对所有的类在加载前进行校验并为每个类通过一个int数值来应用

-XX:+TraceClassLoading :跟踪类加载的信息(诊断内存泄露很有用)

-XX:+TraceClassUnloading :跟踪类卸载的信息(诊断内存泄露很有用)

JVM调优工具Jconsole,jProfile,VisualVM

    Jconsole :JDK自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。

    JProfiler:商业软件,需要付费。功能强大。

    VisualVM:JDK自带,功能强大,与JProfiler类似。推荐 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值