jvm面试都有什么问题?


前两天去美团面试,被问了虚拟机的问题,我有点激动,因为我之前去京东面试的时候被问到虚拟机的问题,我说知道一些,结果面试官问了我知道什么,我就说知道jvm的内存,然后说了一些关于内存的东西,现在想来挺可笑的,因为完全不对,之前已经聊了一会,面试官听完我说的jvm的内存之后直接就跟我说面试结束了,所以我特意准备了jvm的东西,我不是去找的jvm相关面试题去看的,而是完整的看完了周志明先生写的深入理解jvm,不敢说理解有多深,我只是认真的去看了,我也才发现我开始向一个真正的java高级开发转变。
从哪里跌倒爹就从哪里爬起来,从面试来看关于jvm的内存肯定是面试难以避免的问题。我就以我知道的先来说说jvm的内存,并且我不打算再翻开书本去对照了,我就是说说我知道的,甚至谈不上理解。
java堆(JavaHeap)
1.用来存放对象的,几乎所有对象都放在这里,被线程共享的,或者说是被栈共享的
2.堆又可以分为新生代和老年代,实际还有一个区域叫永久代,但是jdk1.7已经去永久代了,所以可以当作没有,永久代是当jvm启动时就存放的JDK自身的类和接口数据,关闭则释放。
新生代可以分为Eden区和两个幸存区,这么设计是为了更好地利用内存 之前的设计是只分为两部分一样一半 后来发现这样只利用到了一半的内存 才改为按比例分成三个区的,使用的是复制回收算法,两个幸存区是较小的区域。逻辑是每次使用Eden区和其中一个幸存区,回收时将其还存活着的对象一次性的复制到另一个幸存区中,最后清理到刚才使用的Eden和其中一个幸存区。
美团的面试官也问了这个问题,他也说了他的理解,我感觉可能是不准确的。
新建对象就在Eden区,Eden就是伊甸,顾名思义。但是并不是对象最活跃的区域,对象最活跃的区域是老年代,因为经过各种垃圾回收之后对象都跑到这里来了。
3.内存溢出
内存溢出其实没什么好讲的,满了就会溢出。怎么才能满呢,不断创建对象,那问题又来了,创建多了被回收怎么办,好办,将新建的对象存到list里去,就不会回收了,为什么呢,因为jvm判定一个对象的死活就是根据对象是不是被引用。
此外堆跟随jvm的,有jvm就有堆。堆也是垃圾回收的主要区域,又叫GC堆,垃圾堆,玩笑。
jvm栈
1.要说栈是用来存什么的,其实我感觉不严谨,栈是运行时创建的,是跟随线程的,它不是用来存什么的,那它用来干什么的,它是用来存栈帧的,没有图不太好说呢,等下我去截个图。

图来了我就不用多说了。每个栈帧其实可以理解为一个方法,我是这么理解的,之间的关系就是调用。
2.栈的好处就是不需要垃圾回收,随着线程结束内存就释放。
3.但是并不是说就不会内存溢出,那么栈的内存溢出是怎么产生的呢,肯定也是满了,这个满了怎么理解呢,一是要申请的不够了,二是jvm内存太小,这是个有趣的问题。但是产生的错误却是不一样的,如果创建一个void方法调用自身,错误是stackoverflowError,如果不断创建线程则会outOfMemoryError。这里就有一个比较高级的问题了,对于第二种多线程内存溢出该怎么解决呢,深入理解jvm一书中给出的解决方案是这样的,通过减小最大堆和栈容量来换取更多的线程。
方法区和运行时常量池
1.方法区是堆的一个逻辑区域,但是又叫非堆。运行时常量池又是方法区的一部分,真正的一部分。方法区并不是存方法的,存方法的应该是栈或者栈帧。方法区存的是类信息、常量、静态变量等,也是被线程共享的区域。运行时常量池存放的是编译期生产的各种字面量和符号引用。
2.这块内存区域的回收没啥好说的,因为我也不太清楚,我只知道HotSpot的设计团队选择把GC分代扩展至方法区了,或者是使用永久代实现方法区。
3.内存是肯定会溢出的,不断创建类会导致方法区内存溢出,而不断将常量放入常量池(String.intern()),常量池也会内存溢出。
内存是jvm的重点区域,也是jvm优化的重点区域,既然这么重要一下子也不可能写完了,慢慢学习补充吧。

 
 
==========================================待解决==========================

 一个GC部分简单的连环炮。

  面试官可以先问你什么时候一个对象会被GC?

什么时候会触发垃圾回收
在之前说到,当对象没人引用

导致新生代的内存占用越来越多,都快满了

这个时候,就会触发垃圾回收

被那些变量的引用的不能被回收
只要你的对象被方法的局部变量,类的静态变量给引用了就不会回收他们

JVM使用了一种可达性分析算法

用来分析对象被谁引用了

在JVM规范中,局部变量就是一个GC Roots

Java中不同的引用类型
强引用
只要是强引用,引用的类型,垃圾回收器,是不会回收这个对象的


软引用
正常情况下垃圾回收是不会碰这样的对象的
但是当垃圾回收之后,发现还是不足以存放新对象
才会把软引用回收掉
那怕被变量引用了


弱引用
如果发生垃圾回收,就会把这种给回收掉


虚引用
很少用

finalize()方法
有GC Roots引用的对象不能回收,没有GC Roots引用的对象可以回收,

如果有GC Roots引用,但是如果是软引用或者弱引用的,也有可能被回收掉。

假设没有GC Roots引用的对象,是一定立马被回收吗

其实不是的,这里有一个finalize()方法可以拯救他自己

 

假设有一个ReplicaManager对象要被垃圾回收了,那么假如这个对象重写了Object类中的finialize()方法

此时会先尝试调用一下他的finalize()方法,看是否把自己这个实例对象给了某个GC Roots变量,比如说代码中就给了ReplicaManager类的静态变量。

如果重新让某个GC Roots变量引用了自己,那么就不用被垃圾回收了。

很少使用

 =================================================

  接着继续问你为什么要在这种时候对象才会被GC?

  接着继续问你GC策略都有哪些分类?

  你如果说出来了,继续问你这些策略分别都有什么优劣势?都适用于什么场景?

  你继续说出来了以后,给你举个实际的场景,让你选择一个GC策略?

  你如果选出来了,继续问你,为什么要选择这个策略?

  下面是关于类加载机制的简单连环炮。

  首先肯定是先问你Java的类加载器都有哪些?

  回答了这些以后,可能会问你每个类加载器都加载哪些类?

  说完以后,可能会问你这些类加载之间的父子关系是怎样的?

  你在回答的时候可能会提到双亲委派模型,那么可以继续问你什么是双亲委派模型?

  你解释完了以后,可能会继续问你,为什么Java的类加载器要使用双亲委派模型?

  你回答完以后,可能会继续问你如何自定义自己的类加载器,自己的类加载器和Java自带的类加载器关系如何处理?

  再来一个关于内存的连环炮。

  首先肯定就是问你内存分为哪几部分,这些部分分别都存储哪些数据?

  然后继续问你一个对象从创建到销毁都是怎么在这些部分里存活和转移的?

  接着可能会问你,内存的哪些部分会参与GC的回收?

  完事以后,可能还会问你Java的内存模型是怎么设计的?

  你回答了以后,还会继续问你为什么要这么设计?

  问完以后,还可能会让你结合内存模型的设计谈谈volatile关键字的作用?

  你在谈的时候,肯定会提到可见性,那么接着可见性这三个字,还可以继续问你并发的内容。

原文地址:https://www.cnblogs.com/huangwentian/p/14693623.html