9.JAVA大厂面试第二季--JVM

前置知识-jvm结构图以及常见GC方法见jvm专栏

jdk1.7以前字符串常量池放在永久代中 ,1.7放入移动到了堆中,并且以后都放在堆中了

1.8移除了永久代添加了元空间,与永久代不同元空间是存在于本地物理内存中

方法区是所有线程共享。主要用于存储类的信息、运行时常量池、方法数据、方法代码,静态变量(属于类而不属于对象)等,它属于一种规范,在不同的虚拟机是不同的叫法。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。对于hotspot虚拟机来讲元空间就是非堆,而在jdk1.7以前永久代就是非堆

问题一:jvm垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots?

什么是垃圾?内存中以及不再被使用到的空间就是垃圾

要进行垃圾回收如何确定一个对象是否可以被回收?引用计数法/枚举根节点做可达性分析

为了解决引用计数法的循环引用问题,java使用了可达性算法,什么是可达性算法?基本思路就是通过一系列名为GC Roots的对象作为起始点开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。也就是给定一个集合的引用作为根触发,通过引用关系遍历对象图,能被遍历到的对象被判定为存活,否则判定为死亡。所谓GC Roots就是一组必须活跃的引用。如图123是存货对象而456是死亡对象。

有哪些对象可以作为GC Roots呢?虚拟机栈(栈帧中的局部变量区,也叫局部变量表)中引用的对象,方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中JNI(native)方法引用的对象

 问题二:有没有做过JVM调优和参数配置,请问如何查看JVM系统默认值?

JVM参数有类型:标配参数,x参数(了解),xx参数

标配参数:-version -help java -showversion

x参数:-Xinit 解释执行 -XCompille 第一次使用就便宜成本地代码 -Xmixed 混合模式

XX参数--

Boolean类型: -XX:+或者-某个属性值  +表示开启 -表示关闭

在VM options里面添加如下语句的效果:

-XX:+PrintGCDetails 开启打印GC细节(jinfo -flag PrintGCDetails 线程号可以查看GC打印细节是否开启)

-XX:+UseSerialGC 是否使用串行垃圾回收器

KV设值类型:

-XX: MetaspaceSize=xxxx 原空间大小设置(同样用jinfo也可以查看)

-XX:MaxTenuringThreshold=xxxx  到达多少年龄可以进入养老区

XX参数之坑题:

-Xms(默认内存)和-Xmx(最大内存)这两个参数属于什么分类?(-Xms1024m)

都是XX参数

-Xms等价于-XX:InitialHeapSize初始化堆内存

-Xmx等价于-XX:MaxHeapSize 最大堆内存

查看参数命令

java -XX:+PrintFlagsInitial 查看初始化参数的值

java -XX:+PrintFlagsFinal -version 查看所有值(区分修改后值)

这个命令同样可以修改值 最后一个T是测试类的名字

普通等号表示没有被修改过

:=说明在加载的时候被jvm动过或者人为修改过

java -XX:+PrintCommandLineFlags -version 依旧是盘点家底的命令,实用点在最后一个命令可以查看当前垃圾回收器的种类

 第三题--怎么获得初始堆内存大小和最大堆内存大小?

 第四题--基本的JVM调优参数都用过哪些?

  • -Xms 初始内存大小,默认不设置的时候为物理内存的1/64,等价于-XX:InitialHeapSize
  • -Xmx 最大分配内存,默认为物理内存的1/4,等价于-XX:MaxHeapSize
  • -Xss 单个线程栈的大小(栈管运行,堆管存储),一般默认为512k-1024k 等价于-XX:ThreadStackSize
  • -Xmn 设置年轻代的大小(一般不去设置)
  • -XX:MetaspaceSize 设置元空间大小,jdk8以后元空间代替永久代,其功能类似于永久代但是元空间并不在虚拟机中二是存在于本地内存,因此元空间大小仅收到本地内存限制。元空间出厂配置默认的很小,大概21m
  • -XX:+PrintGCDetails 打印GC具体信息,其参数解读(GC参数) FullGC参数fullGC的参数规律:GC前内存占用-》GC后内存占用(该区域总内存)
  • -XX:SurvivorRatio 正常新生代堆空间区域配比为Eden:from:to=8:1:1 如果 此参数值设置为4代表Eden:s0:s1=4:1:1
  • -XX:NewRatio 新生代和老年代的比例 比如配置为2 那么新生代:老年代=1:2 新生代占整个堆的1/3
  • -XX:MaxTenuringThreshold 设置垃圾的最大年龄 从yong跑到old需要逃过多少次垃圾回收,默认为15次,并且只能在0-15中调优

第五题--强引用,弱引用,软引用,虚引用分别是什么?

强引用:95%情况下都是强引用。Book book=new Book;当内存不足,JVM开始垃圾回收,对于强引用对象,就算是出现了OOM也不会对该对象进行回收,死都不收。强引用是最常见的普通对象引用,只要还有强引用指向一个对象,就表明对象还活着,垃圾收集器就不会处理这种对象。当我们把一个对象赋给一个引用变量,这个引用变量就是一个强引用。由于该对象被强引用变量引用,它处于可达状态,所以它永远也不会被JVM回收,因此强引用是造成Java内存泄漏的主要原因。那对于这样一个对象什么时候回收呢,如果一个普通的对象,没有其他的引用关系,只要超过了引用作用域或者显示的将强引用赋值为null,一般就认为是可以被垃圾回收的了。(比如下面的obj1,其强引用作用于栈中,而对象存在于堆中,当obj1的作用域结束以后,对象就会回收了)

对于如下的代码,我们依旧能打出obj2 的值,即使我们调用了垃圾回收也不会吧obj2回收走。

软引用内存足够的情况下我不收,但是内存不足的情况下就会被回收。它通常使用在对内存敏感的程序中,比如高速缓存就用到了软引用。假如本地有一个应用需要读取大量本地的图片:如果每次读取图片都要从硬盘读取会严重影响性能,如果一次性全部加载到内存中又可能造成内存溢出,使用软引用就可以解决这个问题。其设计思路是用一个HashMap<String,SoftReference>来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足的时候,JVM会自动回收这些缓存图片所占用的空间,从而有效的避免OOM的问题。

对于如下的代码,如果我们内存足够就可以打印出softReference对象否则软引用对象就是null。

 弱引用:只要垃圾回收机制一运行就直接回收 weakReference(当对象不再可用)

 对于如下代码,打印弱引用对象直接就是null

WeakHashMap的使用,先来看一下普通的HashMap

 打印结果,即使把key置为null,跟哈希Map也没关系,这是因为我们的key是一个强引用

 WeekHashMap的key是一个弱引用

 

虚引用(又称幽灵引用)如果一个对象仅仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。其主要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。换句话说就是在这个对象被收集器回收的时候收到一个系统通知活着后续添加进一步的处理。

对于ReferenceQueue的说明:在创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队列,如果程序发现某个虚引用被加入到引用队列,那么我就可以在所引用对象的内存被回收之前采取必要的行动,相当于一个通知机制。当关联的引用队列中有数据的时候,意味着引用指向堆内存的对象被回收了,通过这种方式,JVM允许我们在对象被销毁之后,做一些我们自己想做的事情:

 如上代码证明了,弱引用被gc回收之后都会被放置到一个ReferenceQueue里

 虚引用代码证明(其实和弱引用是一样的):

 

 小总结:

  • 强引用:obj2引用obj1即使obj1被置空obj2也不回收。
  • 软引用:相对于强引用的情况,如果内存足够如上,内存不够obj2被回收
  • 弱引用:只要obj1=null 立刻回收obj2
  • 虚引用:和ReferenceQueue组合用于通知

第六题--谈谈对OOM的理解--outOfMemoryError

前置内容:关于异常和错误

Exception和Error都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

Exception是程序正常运行中,可以预料的意外情况,可以并且应该被捕获,进行相应处理。

Error是指在正常情况下,很少发生的错误,绝大部分的Error都会导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError,都是Error的子类。

StackOverflowError:栈溢出错误

自己调用自己,直接抛出错误:

 JavaHeapSpace:堆内存不够用

new random().nextint()生成随机数,当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。 

 更简单的new一个大对象直接撑爆

 GC overhead limit exceeded:GC回收时间过长,大量的资源全部都拿去做GC了

 修改直接内存大小

 

DirectBufferMemory:本地物理内存不足

出现的原因,本地的内存不属于GC管辖范围,本地内存的对象们就不会回收,这个时候堆内存充足,但是本地内存可能预计用光了就会报这个错误。

黑框表示全部物理内存24G红框为总内存的1/4--6G ,这里出现的错误就是红框被用光了

 把直接内存分成5M,然后new一个6M的对象,其中的方法key查看直接内存的大小

 unable to create new native thread:达到了创建线程的上限

原因:一个应用进程创建了多个线程,超过系统承载极限,或者你的服务器不允许你创建这么多线程,linux系统默认运行单个进程key创建的线程数是1024个(有误差)

解决方法:降低线程数,或者更改linux服务器配置

例子,一直循环,并且让线程一直运行

 linux运行如下命令

 查看和修改配置

 Metaspace:元空间(方法区)溢出

不停的生成类往元空间里面灌,就可以模拟出这个错误。OOMTest是一个空的静态内部类,这里使用的是CGlib代理模式生成类:

第七题-GC垃圾回收算法和垃圾回收器的关系?

GC算法(分代收集/复制/标记清除/标记整理)是内存回收的方法论,垃圾收集器就是算法的落地实现。以下为四种主流垃圾回收器:

Serial(串行)不适合服务器环境,为单线程设计并且只使用一个线程进行大垃圾回收,会暂停所有的用户线程。但是如果CPU是单核的,他可能更慢。

Parallel(并行)多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算,大数据处理首台处理弱的交互场景。就是串行的加强版,程序还是要停,所用的时间比串行短。

CMS(并发)用户线程和垃圾手机线程同时执行(不一定是并行,可能是交替执行),不需要停顿用户线程,互联网公司多用他,使用对相应时间有要求的场景

G1:将堆内存分割成不同的区域然后并发的对其进行垃圾回收。(java9以后有)

第八题-怎么查看服务器的垃圾回收器?默认的垃圾回收器有哪些?生产上如何配置?谈谈自己对垃圾收集器的理解

使用命令 java -XX:+PrintCommandLineFlags -version查看默认的垃圾收集器。

对名字不一致的做出解释

  • UseSerialGC--》Serial Copying
  • Parallel Scavenge--》UseParallelGC
  • UseParNewGC--》ParNew
  • Serial Old--》已经淘汰不再使用

新生代和老年代可以使用的垃圾回收器,G1两者 都可以使用

垃圾收集器之间的组合,×代表这种组合不可取或者已经淘汰

对七种垃圾回收器做出具体解释:

对于新生代:

Serial收集器:串行收集器:一个单线程的收集器,在进行垃圾手机的时候,必须停止所有其他工作线程一直到他的收集工作结束

串行收集器是最古老,最稳定以及高效率的收集器,虽然在收集垃圾过程中需要暂停所有其他工作线程,但是他简单高效,对于单个CPU环境来说,没有线程交互的开销可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机运行在client模式下默认的新生代垃圾收集器。

ParNew收集器:并行收集器

使用多线程进行垃圾回收在垃圾收集的时候,会暂停其他所有工作线程直到收集结束

ParNew其实就是Serial收集器新生代的并行多线程版本,最常见的场景就是配合老年代的CMS工作,其余的行为和Serial收集器完全一样它是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

 Paraller Scavenge并行收集器

类似于ParNew也是一个新生代的垃圾收集器,使用复制算法,同样是并行的多线程,俗称吞吐量有限收集器。但是他对标的是老年代的Paraller old,

  • 重点关注的是可控制的吞吐量,如果程序运行100分钟,垃圾收集时间1分钟那么吞吐量就是99%,高吞吐量意味着高效利用CPU时间,它多用于在后台运算而不需要太多交互的任务。
  • 此外自适应调节策略也是一个重要区别,它的意思是虚拟机会根据当前系统的运行 情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间,或者最大的吞吐量。

ParallerOld收集器

Paraller Scavenge 的老年代版本,使用多线程的标记-整理算法,在jdk1.6以上才开始提供,在之前Paraller Scavenge只能搭配老年代的Serial Old收集器,只能保证新生代吞吐量优先,无法保证整体的吞吐量。

CMS收集器

CMS(并发标记清除)是一种以获取最短回收停顿时间为目标的收集器,适用于互联网服务器这类重视服务器的响应速度,希望系统停顿时间最短,其组合形式为ParNew+CMS+Serial Old。并发收集低停顿,其中的并发指的是和用户线程一起执行

 一共有四个步骤:

  • 初始标记:只是标记一下GC Roots能直接关联的对象,速度很快,需要暂停用户线程。
  • 并发标记:进行GC Roots跟踪的过长,不需要暂停用户线程,主要标记过程和全部对象。
  • 重新标记:为了修正在并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,需要暂停用户线程。
  • 并发清除:清除GC Roots不可达对象,不需要暂停用户线程,基于标记结果,直接清理对象。

由于耗时最长的并发标记和并发清除过程中,垃圾收集线程key和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发的执行。

优点:并发收集停顿低

缺点:由于并发进行,CMS会增加对堆内存的占用,也就是说CMS必须在老年代堆内存用尽之前完成垃圾回收,否则失败的时候会触发担保机制,串行收集器将会以STW的方式进行一次GC,从而造成较大的停顿时间。并且标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数-XX:CMSFullGCBrForeCompaction来指定多少次收集之后进行一次压缩的Full GC

Serial Old垃圾收集器-基本不用了

老年代的串行收集器,使用标记整理算法,主要有两个用途:

jdk1.5之前版本与新生代的Parallel Scavenge收集器搭配使用。

作为老年代版中使用CMS收集器的后备垃圾收集方案。

如何选择垃圾收集器?

单CPU或者小内存,单机程序 -XX:+SerialGC

多CPU,需要最大吞吐量,如后台计算型应用 -XX:+UseParallelGC或者-XX:+UseParallelOldGC

多CPU,追求低停顿时间,需要快速响应如互联网应用。-XX:+UseConcMarkSweepGC -XX:parNewGC

G1垃圾收集器

简介

总结:

 底层原理:

Region区域化:

 

 回收步骤:

G1的默认参数

CMS和G1的比较:G1不会产生内存碎片并且可以精确控制停顿

第九题--对于JDK自带的JVM监控和性能分析工具用过哪些?一般是怎么用的

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值