1. JMM的两个语义
1)happens before: 一个线程前一个操作对后一个操作可见,具有传递性。确保多线程执行程序结果不变。
2)as if serial: 无论怎么重排序,程序执行结果不变,即不能对数据依赖的操作重排序。确保单线程执行程序结果
2. Minor GC 与 Full GC 分别在什么时候发生?
新生代内存不够用时候发生 MGC 也叫 YGC, JVM 内存不够的时候发生 FGC
3. 什么时候会触发FullGC
除直接调用System.gc外,触发Full GC执行的情况有如下四种。
4. 简述 java 内存分配与回收策略以及 Minor GC 和 Major GC
1)java 内存分配与回收策略
1. 对象优先在堆的 Eden 区分配
2. 大对象直接进入老年代
3. 长期存活的对象将直接进入老年代
2)Minor GC 和 Major GC
当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次 Minor GC。Minor Gc 通常发生在新生代的 Eden 区,在这个区的对象生存期短,往往发生 Gc 的频率较高,回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代 GC的时候不会触发 Minor GC,但是通过配置,可以在 Full GC 之前进行一次 MinorGC 这样可以加快老年代的回收速度。
Minor GC、Young GC、Full GC、Old GC、Major GC、Mixed GC傻傻分不清_背井的博客-CSDN博客_minorgc和younggc
5. JVM的永久代中会发生垃圾回收么?
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确
的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区 (注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)
6. 为什么使用元空间替换永久代?
为了避免OOM异常。因为通常使用PermSize和MaxPermSize设置永久代的大小就决定了永久代的上限,但是不是总能知道应该设置为多大合适, 如果使用默认值很容易遇到OOM错误。当使用元空间时,可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制啦。
7. 你能保证 GC 执行吗?
不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC的执行。
8. SafePoint 是什么
GC 的时候必须要等到 Java 线程都进入到 safepoint 的时候 VMThread 才能开始执行 GC,
1) 循环的末尾 (防止大循环的时候一直不进入 safepoint,而其他线程在等待它进入
safepoint)
2) 方法返回前
3) 调用方法的 call 之后
4) 抛出异常的位置
9. 调优命令
10. 常见调优工具
11. JVM性能调优参数?(简)
12. JVM调优参数?(复)
13. 如何选择垃圾收集器?
14. 怎么获取 Java 程序使用的内存?堆使用的百分比?
可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory()方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。
15. Java会存在内存泄漏吗?
内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。理论上来说, Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收 掉,自动从内存中清除。但是,即使这样,Java也还是存在着内存泄漏的情况,java导致内存泄露的原因 很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露, 尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导 致不能被回收,这就是java中内存泄露的发生场景。
原因:
让我们用下面的例子来看看为什么会发生内存泄露。如下图所示,对象A引用对象B,A的生命周期(t1-t4)比B的生命周期(t2-t3)要长,当B在程序中不再被使用的时候,A仍然引用着B。在这种情况下,垃圾回收器是不会回收B对象的,这就可能造成了内存不足问题,因为A可能不止引用着B对象,还可能引用其它生命周期比A短的对象,这就造成了大量无用对象不能被回收,且占据了昂贵的内存资源。同样的,B对象也可能引用着一大堆对象,这些被B对象引用着的对象也不能被垃圾回收器回收,所有的这些无用对象消耗了大量内存资源。
怎样阻止内存泄露:
16. 内存溢出和内存泄漏的区别:
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of
memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak 会最终会导致 out of memory
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。
17. 硬件内存
18. 缓存一致性问题
19. 指令重排序问题
20. Java内存模型和硬件内存架构之间的桥接
Java内存模型与硬件内存架构之间存在差异。硬件内存架构没有区分线程栈和堆。对于硬件,所有的线程栈和堆都分布在主内存中。部分线程栈和堆可能有时候会出现在CPU缓存中和CPU内部的寄存器中。如下图所示:
21. JVM类加载机制有哪些?
- 全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
- 双亲委派机制, 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
22. JVM内存分配策略
23. 什么情况下会触发Full GC?
24. JDK自带的定位问题的工具?
- jps jps是jdk提供的一个查看当前java进程的小工具, 可以看做是JavaVirtual Machine Process Status Tool的缩写。
jps –l # 输出输出完全的包名,应用主类名,jar的完全路径名
- jstack jstack是jdk自带的线程堆栈分析工具,使用该命令可以查看或导出 Java 应用程序中线程堆栈信息。
# 基本
jstack 2815
jstack -m 2815 # java和native c/c++框架的所有栈信息
jstack -l 2815 # 额外的锁信息列表,查看是否死锁
- jinfo jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息
jinfo 2815 # 输出当前 jvm 进程的全部参数和系统属性
- jstat jstat参数众多,但是使用一个就够了
jstat -gcutil 2815 1000