JAVA基础篇面试题
1. 什么是GC Roots
GC Roots是一组活跃的引用;常用于判断对象是否被回收的可达性分析法中;
可达性分析:通过一系列称为GC Roots 的跟对象作为起始节点集,从这些结点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到根节点间没有任何引用链相连,则证明此对象可再被使用。
能够作为GC Roots对象:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、布局变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
- 在方法区中常量引用对象,譬如字符串常量池里的引用。
- 在本地方法栈JNI(Native)引用的对象。
- Java虚拟机内部的引用,如基本数据类型对应的Class现象,一些常驻的异常对象(比如NullPointException、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized关键字)持有的对象。
- 反应Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
- 其他对象临时性的加入,共同构成GC Roots集合。
在HotSpot虚拟机中,GC Roots中一般为全局性引用(常量或类静态属性)和执行上下文(栈帧中的本地变量表)中;
2. JVM调优和参数配置
JVM参数类型:
-
标配参数(了解): java -version
java -help
java -showversion
-
x参数(了解): -Xint
-Xcomp
-Xmixed
-
xx参数:
-
Boolean类型 -XX:+或者-某个属性值
+表示开启 -表示关闭
java -jar -XX:+PrintGCDetails 某个.jar
jinfo -flag PrintGCDetails(参数) pid
-XX:+UseSerialGC
-
KV设置类型 -XX:属性Key=属性值Value
-XX:MetaspaceSize=128m
-XX:MaxTenuringThreshold=15
jinfo -flags pid
-
注意 -Xms
-Xmx
-
其余默认参数 -XX:+PrintFlagsInitial
java -XX:+PrintFlagsInitial
-XX:+PrintFlagsFinal
java -XX:+PrintFlagsFinal -version
java -XX:+PrintFlagsFinal -jar xxx.jar
java -XX:+PrintCommandLineFlags -version
3. 常用的JVM调优参数
-Xss
-Xmn
-XX:MetaspaceSize
-XX:MaxDirectMemorySize
-XX:+PrintGCDetails
-XX:SurvivorRatio
默认-XX:SurvivorRatio=8
若设置
-XX:SurvivorRatio=4, Eden:SO:S1=4:1:1
SurvivorRatio值就是设置Eden区的比例,S0和S1相同
-XX:NewRatio
默认 -XX:NewRatio=2
若配置
-XX:NewRatio=4
NewRatio值是老年代的占比,新生代占1
-XX:MaxTenuringThreshold
如果设置为0的话,年轻代对象不经过Survivor区,直接进入老年代,如果将值设的偏大,则可以让对象在年轻代存活时间长;只能在[0~15]次内设置,不能设置不在区间的值;
-Xms128m
-Xmx4096m
-Xss1024k
-XX:MetaspaceSize=512m
-XX:+PrintCommandLineFlags
-XX:+PrintGCDetails
-XX:+UseSerialGC
-Dio.netty.leakDetectionLevel=PARANOID
-agentlib:jdwp=transport=dt_socket,address=0,suspend=n,server=y
-server
-Xmx512m
-XX:MaxPermSize=256m
-Xnoclassgc
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSClassUnloadingEnabled
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=80
-XX:+PrintClassHistogram
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-Xloggc:gc.log
-jar game-logic.jar
4. 分析GC日志
[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->1034K(9728K), 0.0063607 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 2156K->512K(2560K)] 2687K->1527K(9728K), 0.0019089 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 512K->512K(2560K)] 1527K->1607K(9728K), 0.0011604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 512K->0K(2560K)] [ParOldGen: 1095K->1279K(7168K)] 1607K->1279K(9728K), [Metaspace: 3476K->3476K(1056768K)], 0.0149073 secs] [Times: user=0.20 sys=0.00, real=0.02 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 1279K->1279K(9728K), 0.0009314 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 1279K->1252K(7168K)] 1279K->1252K(9728K), [Metaspace: 3476K->3476K(1056768K)], 0.0268015 secs] [Times: user=0.20 sys=0.00, real=0.03 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.wooduan.hall.rpc.automation.test.TestJVM.main(TestJVM.java:11)
Heap
PSYoungGen total 2560K, used 207K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 10% used [0x00000000ffd00000,0x00000000ffd33e28,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 7168K, used 1252K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 17% used [0x00000000ff600000,0x00000000ff739118,0x00000000ffd00000)
Metaspace used 3550K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 388K, capacity 390K, committed 512K, reserved 1048576K
5. 四种引用
强引用
概念:当内存不足时,JVM开始内存回收,对于强引用的对象,就算出现了OOM也不会对该对象进行回收;
说明:最常见的一种对象引用方式,只要有强引用指向一个对象就保证对象还活着,处于可达状态,垃圾回收器是不会回收此类对象。即使该对象永远不会被使用,JVVM也不会回收,因此是造成Java内存泄漏的主要原因之一;
软引用
概念:一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference 类实现,当系统内存足够时,不会被回收,当系统内存不足时才会被回收;
用途:通常在对内存使用很频繁的程序中,例如高速缓存就有用到软引用;、
场景:需要读大量的本地图片
- 如果每次从硬盘加载会严重影响性能;
- 如果一次全部加载会造成内存溢出;
因此这种情况可以使用软引用去解决;通过一个HashMap保存图片的路径和相应图片对象关联的引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片所咱占用的空间,有效避免了OOM(OutOfMemory )的问题;
public static void testSoftReference(String[] args) {
Object o = new Object();
SoftReference<Object> softReference = new SoftReference<>(o);
System.out.println("前:"+o);
System.out.println("前:"+softReference.get());
o = null;
try{
byte[] bytes = new byte[5*1024*1024];
} catch (Throwable ignore){
System.out.println("异常");
} finally {
System.out.println("后:"+o);
System.out.println("后:"+softReference.get());
}
}
弱引用
概念:只要有GC回收,就被回收;
public static void weakReference(String[] args) {
Object o = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o);
System.out.println("前:"+o);
System.out.println("前:"+weakReference.get());
o = null;
try{
System.gc();
} finally {
System.out.println("后:"+o);
System.out.println("后:"+weakReference.get());
}
}
常见应用:WeakHashMap ,key对象为弱引用,当GC后回收;
public static void weakHashMap(String[] args) {
Map<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(1);
map.put(key, "hh");
System.out.println(map);
key = null;
System.gc();
System.out.println(map);
}
{
Map<Integer, String> map = new WeakHashMap<>();
map.put(new Integer(2), "hh");
System.out.println(map);
System.gc();
System.out.println(map);
}
虚引用
概念:PhantomReference 不会决定对象的生命周期,如果对象仅持有虚引用,那么他和没有任何引用一样,在任何时候都可能被垃圾回收器回收,不能单独通过他访问对象,虚引用必须与引用队列ReferenceQuene 一起使用;
作用:跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被finalize后,做某些事情的机制。其get()方法总是返回null,因此无法访问对应被引用对象;在被回收后,可以通过其queue获取到被回收的引用对象;
意义:说明一个对象已经进入Finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作;设置虚引用关联的唯一目的在于对这个对象被回收时收到一个系统通知或者后续添加进一步的处理。Java允许使用finalize()方法在垃圾收集器将对象从内存中清除前做必要的清理工作(类似AOP的后置通知)。
引用队列:在引用的对象Object@6d6f6e28 被回收时,会将引用对象WeakReference@135fbaa4 放入队列中;
public static void referenceQueue(String[] args) {
Object o = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(o, queue);
System.out.println("前 对象地址: "+o);
System.out.println("前 弱引用地址: "+weakReference);
System.out.println("前 弱引用对象地址: "+weakReference.get());
System.out.println("前 引用队列中的地址: "+queue.poll());
o = null;
System.gc();
System.out.println("后 对象地址: "+o);
System.out.println("后 弱引用地址: "+weakReference);
System.out.println("后 弱引用对象地址: "+weakReference.get());
System.out.println("后 引用队列中的地址: "+queue.poll());
}
虚引用代码:
public static void phantomReference(String[] args) {
Object o = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o, queue);
System.out.println("前 对象地址: "+o);
System.out.println("前 引用地址: "+phantomReference);
System.out.println("前 引用的对象地址: "+phantomReference.get());
System.out.println("前 引用队列元素地址: "+queue.poll());
o = null;
System.gc();
System.out.println("后 对象地址: "+o);
System.out.println("后 引用地址: "+phantomReference);
System.out.println("后 引用的对象地址: "+phantomReference.get());
System.out.println("后 引用队列元素地址: "+queue.poll());
}
6. 常见的JVM异常/错误
异常Throwable分为Error和Exception,错误和异常
-
StackOverflowError: 栈空间溢出错误,死循环等会抛出; -
OutOfMemoryError:java heap space: 堆内存溢出错误; -
OutOfMemoryError:GC overhead limit exceeded:GC回收时间过长,连续多次超过了98%的时间用来做GC并且回收了不到2%的堆内存才会抛出,如果不抛错误的后果就是GC清理的内存很快被再次填满,迫使继续GC,形成死循环,造成的结果就是CPU100%,没有回收到空间; -
OutOfMemoryError:Direct buffer memory:直接内存不足(直接内存使用的是本地内存);由于不断分配本地内存,而堆内存使用少,因此JVM无需执行GC,DirectByteBuffer对象们不会被回收,因此,当本地空间使用完后再次分配内存就会出现该错误;
导致的原因:
写NIO程序时经常使用ByteBuffer来读取或写入数据,这是基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储再Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中提高性能,并且避免了JAVA堆和Native堆来回复制数据;
ByteBuffer.allocate(capability) 分配JVM堆内存,属于GC管辖范围,但需要在两个堆中拷贝数据;
ByteBuffer.allocateDirect(capability) 分配OS本地内存,不属于GC管辖,不需要内存拷贝,速度较快;
堆外内存通常是本机内存的1/4;
-
OutOfMemoryError:unable to create new native thread:这个异常和平台有关,由于一个进程创建的线程数超过了系统所能承载的线程数,就会报错,例如linux允许单个进程可创建的线程数是1024个(root账户无上限);遇到这种问题,要么优化代码降低线程数,要么修改系统配置,扩大linux默认限制;
linux查看与修改线程数:
查看:ulimit -n
修改,重启生效:
/etc/security/limits.d/90-nproc.conf文件尾添加
? soft nproc 204800
? hard nproc 204800 /etc/security/limits.d/def.conf文件尾添加
? soft nofile 204800
? hard nofile 204800
-
OutOfMemoryError:Metaspace:Java8以后使用Metaspace代替永久代,他是方法区在HotSpot中的实现,他与持久代的区别就是元空间并不在JVM内存中,而是使用的是本地内存;
|