java之jvm

1.JVM内存模型java

线程独占:栈,本地方法栈,程序计数器
线程共享:堆,方法区
linux

回答以上问题是需回答两个要点:
1. 各部分功能
2. 是不是线程共享程序员

2.JMM与内存可见性
JMM是定义程序中变量的访问规则,线程对于变量的操做只能在本身的工做内存中进行,而不能直接对主内存操做.因为指令重排序,读写的顺序会被打乱,所以JMM须要提供原子性,可见性,有序性保证.算法

3.类加载与卸载编程

加载机制-双亲委派模式bootstrap

双亲委派模式,即加载器加载类时先把请求委托给本身的父类加载器执行,直到顶层的启动类加载器.父类加载器可以完成加载则成功返回,不能则子类加载器才本身尝试加载.
优势:api

避免类的重复加载
避免Java的核心API被篡改缓存


分代回收
分代回收基于两个事实:大部分对象很快就不使用了,还有一部分不会当即无用,但也不会持续很长时间.安全

年轻代->标记-复制
老年代->标记-清除网络

回收算法
1.CMS算法
1.7前主流垃圾回收算法,为标记-清楚算法,优势是并发收集,停顿小.

2.G1算法
1.9后默认的垃圾回收算法,特色保持高回收率的同时减小停顿.采用每次只清理一部分,而不是清理所有的增量式清理,以保证停顿时间不会过长

3.ZGC
1.11中提供的高效垃圾回收算法,针对大堆内存设计,能够处理TB级别的堆,能够作到10ms如下的回收停顿时间.

考察点
深刻理解JVM内存模型
了解类加载机制
了解内存可见性
了解经常使用的GC算法实现和适用场景
能根据业务场景选择合适JVM参数和GC算法

加分项
编译器优化
问题排查经验与思路
JVM调优经验和调优思路
了解最新的技术趋势(ZGC和Graalvm)

真题汇总
1.简单描述一下JVM的内存模型
jvm内存模型:方法区、堆、程序计数器 、本地方法栈、虚拟机栈
这里针对方法区、栈、堆、程序计数器作一个说明
这里咱们来讲个流程:
程序(非多线程)开始运行的时候,在系统中会自动分配一个栈,这个时候程序计数器就开始起到做用了,它会指示jvm对编译以后的字节码的执行方向,同时在执行一个方法的时候就会在栈中分配一个属于方法一个栈帧,方法的局部变量都会存放在这个栈帧中,其生命周期随着方法的结束而释放,这里强调一点的是先进后出的逻辑,堆中的数据当没有对象引用的时候就成了孤立数据,此时就会被GC垃圾回收器对其进行内存释放。
方法区包含了常量池:存放类信息、常量、静态变量、即时编译器编译后的代码等。其中静态成员变量在类装载的时候就进行了建立,在整个程序结束时按序销毁。

静态成员变量在类装载的时候就进行了建立,在整个程序结束时按序销毁。
实例变量在类实例化对象时候建立,在对象销毁的时候销毁。
局部变量在局部范围使用时建立,跳出局部范围销毁。

这里咱们说个题外话,在术语中常常会听到编译期和运行期
编译期:就是将源码编译成二进制的.class字节码文件,并将文件放到了磁盘中,编译期至关于只是作了一个翻译的过程
运行期:这块就是咱们java解释器将二进制.class字节码解释成程序能识别的程序(将磁盘中的代码放到内存中就是类加载过程)


2.何时会触发FullGC?

触发MinorGC(Young GC)
虚拟机在进行minorGC以前会判断老年代最大的可用连续空间是否大于新生代的全部对象总空间

一、若是大于的话,直接执行minorGC

二、若是小于,判断是否开启HandlerPromotionFailure,没有开启直接FullGC

三、若是开启了HanlerPromotionFailure, JVM会判断老年代的最大连续内存空间是否大于历次晋升的大小,若是小于直接执行FullGC

四、若是大于的话,执行minorGC

触发FullGC
老年代空间不足
若是建立一个大对象,Eden区域当中放不下这个大对象,会直接保存在老年代当中,若是老年代空间也不足,就会触发Full GC。为了不这种状况,最好就是不要建立太大的对象。

持久代空间不足
若是有持久代空间的话,系统当中须要加载的类,调用的方法不少,同时持久代当中没有足够的空间,就出触发一次Full GC

YGC出现promotion failure
promotion failure发生在Young GC, 若是Survivor区当中存活对象的年龄达到了设定值,会就将Survivor区当中的对象拷贝到老年代,若是老年代的空间不足,就会发生promotion failure, 接下去就会发生Full GC.

统计YGC发生时晋升到老年代的平均总大小大于老年代的空闲空间
在发生YGC是会判断,是否安全,这里的安全指的是,当前老年代空间能够容纳YGC晋升的对象的平均大小,若是不安全,就不会执行YGC,转而执行Full GC。

显示调用System.gc


3.Java类加载器有几种,关系怎样的?
JAVA类加载器包括几种?
引导类加载器 bootstrap class loader
  启动类加载器主要加载的是JVM自身须要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 /lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必因为虚拟机是按照文件名识别加载jar包的,如rt.jar,若是文件名不被虚拟机识别,即便把jar包丢到lib目录下也是没有做用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类

扩展类加载器 extensions class loader
  它负责加载JAVA_HOME/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者能够直接使用标准扩展类加载器。

应用程序类加载器 application class loader
  应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是咱们常常用到的classpath路径,开发者能够直接使用系统类加载器,通常状况下该类加载是程序中默认的类加载器,经过ClassLoader#getSystemClassLoader()方法能够获取到该类加载器。

自定义类加载器 java.lang.classloder
  就是自定义啦,经过继承java.lang.ClassLoader类的方式

类加载器之间的关系
  启动类加载器,由C++实现,没有父类。
  拓展类加载器(ExtClassLoader),由Java语言实现,父类加载器为null
  系统类加载器(AppClassLoader),由Java语言实现,父类加载器为ExtClassLoader
  自定义类加载器,父类加载器确定为AppClassLoader。

如何自定义一个类加载器?
  经过继承ClassLoad定义一个类加载器。

应用场景
  如Tomcat容器,每一个WebApp有本身的ClassLoader,加载每一个WebApp的ClassPath路径上的类,一旦遇到Tomcat自带的Jar包就委托给CommonClassLoader加载。同包的隔离。另外成熟的开源框架,都有本身的classloade。


4.双亲委派机制的加载流程是怎样的,有什么好处?
双亲委派机制
  请注意双亲委派模式中的父子关系并不是一般所说的类继承关系。
  其工做原理的是:若是一个类加载器收到了类加载请求,它并不会本身先去加载,而是把这个请求委托给父类的加载器去执行,若是父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,若是父类加载器能够完成类加载任务,就成功返回,假若父类加载器没法完成此加载任务,子加载器才会尝试本身去加载,这就是双亲委派模式,即每一个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子本身想办法去完成。

双亲委派机制做用
  经过这种层级关能够避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设经过网络传递一个名为java.lang.Integer的类,经过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会从新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样即可以防止核心API库被随意篡改

双亲委派模式的工做原理的是;若是一个类加载器收到了类加载请求,它并不会本身先去加载,而是把这个请求委托给父类的加载器去执行,若是父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,若是父类加载器能够完成类加载任务,就成功返回,假若父类加载器没法完成此加载任务,子加载器才会尝试本身去加载,这就是双亲委派模式,即每一个儿子都不肯意干活,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子本身想办法去完成,这不就是传说中的双亲委派模式.那么这种模式有什么做用呢?

双亲委派模式优点

采用双亲委派模式的是好处是Java类随着它的类加载器一块儿具有了一种带有优先级的层次关系,经过这种层级关能够避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设经过网络传递一个名为java.lang.Integer的类,经过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会从新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样即可以防止核心API库被随意篡改。


5.1.8为何用Metaspace替换掉PermGen?Meatspace保存在哪?
1. PermGen空间的情形
这部份内存空间彻底被移除 PermSize和MaxPermSize JVM参数被忽略掉,若是存在的话启动时会发出警告。

2. Metaspace空间分配模型
1) 大部分类的metadata如今分配到本地内存。
2) 用来描述类的metadata的类已被删除。

3. Metaspace容量
1) 默认状况下,类的metadata的分配是受本机可用内存的限制(固然容量要取决于你使用的32位仍是64位的JVM以及操做系统可用的虚拟内存)。
2) 一个新的参数可用了(MaxMetaspaceSize),容许你限制本地用于类的metadata的内存容量。若是你没有指定这个参数,Metaspace将会在运行时根据应用的需求自动调整大小。

4. Metaspace垃圾回收
1) 一旦类的metadata使用达到“MaxMetaspaceSize”,死的类和类加载器的垃圾回收器会被触发。
2) 适当的Metaspace的监控&调优将明显地被要求以限制此类垃圾回收的频率或延迟。过分的Metaspace垃圾收集多是您的应用程序中类,类加载器内存泄漏或容量不够的症状。

5. Java 堆空间的影响
一些其余数据已迁移到Java堆空间。这意味着下一个将来的JDK 8升级您可能会看到Java堆空间的增长。

6. Metaspace监控
1) Metaspace的使用状况能够从HotSpot 1.8的verbose GC log输出。
2) jstat & jVisualVM


6.编译器会对指令作哪些优化?(简答描述编译器的指令重排)

引言:在Java中看似顺序的代码在JVM中,可能会出现编译器或者CPU对这些操做指令进行了从新排序;在特定状况下,指令重排将会给咱们的程序带来不肯定的结果..

1.  什么是指令重排?

      在计算机执行指令的顺序在通过程序编译器编译以后造成的指令序列,通常而言,这个指令序列是会输出肯定的结果;以确保每一次的执行都有肯定的结果。可是,通常状况下,CPU和编译器为了提高程序执行的效率,会按照必定的规则容许进行指令优化,在某些状况下,这种优化会带来一些执行的逻辑问题,主要的缘由是代码逻辑之间是存在必定的前后顺序,在并发执行状况下,会发生二义性,即按照不一样的执行逻辑,会获得不一样的结果信息。

 2.  数据依赖性

    主要指不一样的程序指令之间的顺序是不容许进行交互的,便可称这些程序指令之间存在数据依赖性。

    主要的例子以下:

名称 代码示例 说明
写后读 a = 1;b = a; 写一个变量以后,再读这个位置。
写后写 a = 1;a = 2; 写一个变量以后,再写这个变量。
读后写 a = b;b = 1; 读一个变量以后,再写这个变量。
 进过度析,发现这里每组指令中都有写操做,这个写操做的位置是不容许变化的,不然将带来不同的执行结果。
  编译器将不会对存在数据依赖性的程序指令进行重排,这里的依赖性仅仅指单线程状况下的数据依赖性;多线程并发状况下,此规则将失效。

3.  as-if-serial语义

   无论怎么重排序(编译器和处理器为了提升并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵照as-if-serial语义。

   分析:  关键词是单线程状况下,必须遵照;其他的不遵照。

   代码示例:

double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r * r; //C
 分析代码:   A->C  B->C;  A,B之间不存在依赖关系; 故在单线程状况下, A与B的指令顺序是能够重排的,C不容许重排,必须在A和B以后。
结论性的总结:
   as-if-serial语义把单线程程序保护了起来,遵照as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员建立了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使单线程程序员无需担忧重排序会干扰他们,也无需担忧内存可见性问题。
   核心点仍是单线程,多线程状况下不遵照此原则。

4. 在多线程下的指令重排
   首先咱们基于一段代码的示例来分析,在多线程状况下,重排是否有不一样结果信息:

class ReorderExample {
int a = 0;
boolean flag = false;

public void writer() {
a = 1; //1
flag = true; //2
}

Public void reader() {
if (flag) { //3
int i = a * a; //4
……
}
}
}
上述的代码,在单线程状况下,执行结果是肯定的, flag=true将被reader的方法体中看到,并正确的设置结果。 可是在多线程状况下,是否仍是只有一个肯定的结果呢?
假设有A和B两个线程同时来执行这个代码片断, 两个可能的执行流程以下:

   可能的流程1, 因为1和2语句之间没有数据依赖关系,故二者能够重排,在两个线程之间的可能顺序以下: 
   可能的流程2:, 在两个线程之间的语句执行顺序以下:
根据happens- before的程序顺序规则,上面计算圆的面积的示例代码存在三个happens- before关系:

A happens- before B;
B happens- before C;
A happens- before C;
这里的第3个happens- before关系,是根据happens- before的传递性推导出来的 
    在程序中,操做3和操做4存在控制依赖关系。当代码中存在控制依赖性时,会影响指令序列执行的并行度。为此,编译器和处理器会采用猜想(Speculation)执行来克服控制相关性对并行度的影响。以处理器的猜想执行为例,执行线程B的处理器能够提早读取并计算a*a,而后把计算结果临时保存到一个名为重排序缓冲(reorder buffer ROB)的硬件缓存中。当接下来操做3的条件判断为真时,就把该计算结果写入变量i中。从图中咱们能够看出,猜想执行实质上对操做3和4作了重排序。重排序在这里破坏了多线程程序的语义。

     核心点是:两个线程之间在执行同一段代码之间的critical area,在不一样的线程之间共享变量;因为执行顺序、CPU编译器对于程序指令的优化等形成了不肯定的执行结果。

5.  指令重排的缘由分析
    主要仍是编译器以及CPU为了优化代码或者执行的效率而执行的优化操做;应用条件是单线程场景下,对于并发多线程场景下,指令重排会产生不肯定的执行效果。

6.  如何防止指令重排
    volatile关键字能够保证变量的可见性,由于对volatile的操做都在Main Memory中,而Main Memory是被全部线程所共享的,这里的代价就是牺牲了性能,没法利用寄存器或Cache,由于它们都不是全局的,没法保证可见性,可能产生脏读。
    volatile还有一个做用就是局部阻止重排序的发生,对volatile变量的操做指令都不会被重排序,由于若是重排序,又可能产生可见性问题。
    在保证可见性方面,锁(包括显式锁、对象锁)以及对原子变量的读写均可以确保变量的可见性。可是实现方式略有不一样,例如同步锁保证获得锁时从内存里从新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。

7.  可见性
    这里提到的可见性是指前一条程序指令的执行结果,能够被后一条指令读到或者看到,称之为可见性。反之为不可见性。这里主要描述的是在多线程环境下,指令语句之间对于结果信息的读取即时性。


7.简单描述一下volatile能够解决什么问题?如何作到的?

 

8.简单描述一下GC的分代回收?

为何要进行分代回收?
JVM使用分代回收测试,是由于:不一样的对象,生命周期是不同的。所以不一样生命周期的对象采用不一样的收集方式。
能够提升垃圾回收的效率。
Java程序运行过程当中,会产生大量的对象,其中有些对象是与业务相关的。好比Http请求的Session对象,线程,Socket
链接等。可是还有一些对象,主要是程序运行过程当中生成的临时变量(好比方法中的局部变量),这些对象生命周期会比较短,
好比:String对象,因为其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次便可回收。

试想,在不进行对象存活时间区分的状况下,每次垃圾回收都是对整个堆空间进行回收,花费时间相对会
长,同时,由于每次回收都须要遍历全部存活对象,但实际上,对于生命周期长的对象而言,这种遍历是没有
效果的,由于可能进行了不少次遍历,可是他们依旧存在。所以,分代垃圾回收采用分而治之的思想,进行代的划
分,把不一样生命周期的对象放在不一样代上,不一样代上采用最适合它的垃圾回收方式进行回收。

如何分代


以下图所示
虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代
(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系
不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

年轻代
全部新生成的对象首先都是放在年轻代的。年轻代的目标就是尽量快速的收集掉那些生命周期短的对象。
年轻代分三个区。一个Eden区,两个Survivor区(通常而言)。大部分对象在Eden区中生成。当Eden区满时,还
存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外
一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的而且此时还存活的对象,将
被复制“年老区(Tenured)”。须要注意,Survivor的两个区是对称的,没前后关系,因此同一个区中可能同时
存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去
过来的对象。并且,Survivor区总有一个是空的。同时,根据程序须要,Survivor区是能够配置为多个的(多于
两个),这样能够增长对象在年轻代中的存在时间,减小被放到年老代的可能。

老年代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。所以,能够认为年老代中存放的
都是一些生命周期较长的对象。

持久代
用于存放静态文件,现在Java类、方法等。持久代对垃圾回收没有显著影响,可是有些应用可能动态生成或
者调用一些class,例如Hibernate等,在这种时候须要设置一个比较大的持久代空间来存放这些运行过程当中新
增的类。持久代大小经过-XX:MaxPermSize=进行设置。

什么状况下触发垃圾回收
因为对象进行了分代处理,所以垃圾回收区域、时间也不同。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC
通常状况下,当新对象生成,而且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,
清除非存活对象,而且把尚且存活的对象移动到Survivor区。而后整理Survivor的两个区。这种方式的GC是对
年轻代的Eden区进行,不会影响到年老代。由于大部分对象都是从Eden区开始的,同时Eden区不会分配的很
大,因此Eden区的GC会频繁进行。于是,通常在这里须要使用速度快、效率高的算法,使Eden去能尽快空闲
出来。

Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC由于须要对整个对进行回收,因此比Scavenge
GC要慢,所以应该尽量减小Full GC的次数。在对JVM调优的过程当中,很大一部分工做就是对于FullGC的调
节。有以下缘由可能致使Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用


9.G1与CMS的区别?
一、CMS收集器

  CMS收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法实现,它的运做过程以下:


1)初始标记  2)并发标记 3)从新标记  4)并发清除


  初始标记、重新标记这两个步骤仍然须要“stop the world”,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,熟读很快,并发标记阶段就是进行GC Roots Tracing,而从新标记阶段则是为了修正并发标记期间因用户程序继续运做而致使标记产生表动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长点,但远比并发标记的时间短。


优势:并发收集、低停顿。

缺点:

1)CMS收集器对CPU资源很是敏感。在并发阶段,它虽然不会致使用户线程停顿,可是会由于占用了一部分线程而致使应用程序变慢,总吞吐量会下降。

2)CMS收集器没法处理浮动垃圾,可能会出现“Concurrent Mode Failure(并发模式故障)”失败而致使Full GC产生。

3)CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,每每会出现老年代还有很大空间剩余,可是没法找到足够大的连续空间来分配当前对象,不得不提早触发一次Full GC。

二、G1收集器

G1是一款面向服务端应用的垃圾收集器。G1具有以下特色:

一、并行于并发:G1能充分利用CPU、多核环境下的硬件优点,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其余收集器本来须要停顿Java线程执行的GC动做,G1收集器仍然能够经过并发的方式让java程序继续执行。

二、分代收集:虽然G1能够不须要其余收集器配合就能独立管理整个GC堆,可是仍是保留了分代的概念。它可以采用不一样的方式去处理新建立的对象和已经存活了一段时间,熬过屡次GC的旧对象以获取更好的收集效果。

三、空间整合:与CMS的“标记--清理”算法不一样,G1从总体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

四、可预测的停顿:这是G1相对于CMS的另外一个大优点,下降停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,


五、G1运做步骤:
一、初始标记;二、并发标记;三、最终标记;四、筛选回收

上面几个步骤的运做过程和CMS有不少类似之处。

初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,而且修改TAMS的值,让下一个阶段用户程序并发运行时,能在正确可用的Region中建立新对象,这一阶段须要停顿线程,可是耗时很短,

并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段时耗时较长,但可与用户程序并发执行。

最终标记阶段则是为了修正在并发标记期间因用户程序继续运做而致使标记产生变更的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remenbered Set Logs里面,最终标记阶段须要把Remembered Set Logs的数据合并到Remembered Set Logs里面,最终标记阶段须要把Remembered Set Logs的数据合并到Remembered Set中,这一阶段须要停顿线程,可是可并行执行。

最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所指望的GC停顿时间来制定回收计划。


10.对象引用有哪几种,有什么特色?
在jdk1.2以前,java中引用的定义:若是引用类型的数据类型中存储的数值表明的是一块内存的起始地址,就称这块内存表明一个引用。在jdk1.2以后,java引入了4种对象引用类型,级别由高到低分别是:强引用、软引用、弱引用和虚引用。

为何须要不一样的引用

对于须要长期运行的应用程序来讲,若是无用的对象所占的内存空间没法及时释放的话,那么在一个局部的时间段里就会造成事实上的内存泄露,若是要及时地释放内存,在java中最稳妥的方式就是在使用完对象事后 当即执行“object = null”,固然这这是理想状态。

经过这4种引用类型 强行调用垃圾回收方法 System.gc() 来解决内存泄露的问题,不一样的引用方式会有不一样的做用,下面咱们来一一讲解。

强引用(StrongReference):直接引用对象,内存不足时也不会回收
软引用(SoftReference):内存不足时,回收引用相关联的对象
弱引用 (WeakReference):不管内存是否充足,都回收引用相关联的对象
虚引用(PhantomReference):任什么时候候均可以被垃圾回收器回收
强引用
强引用是平常编程中最经常使用的一种引用类型,它的特色是只要引用存在,就永远不会调用垃圾回收方法对其进行释放内存,java虚拟机宁肯出现OutOfMemoryError错误中止运行,也会保存内存空间。只用当这个对象的引用被释放掉,对象才会被释放。
正是由于强引用具有这样的特色,因此咱们的开发原则是尽可能少实例化对象。
强引用是形成java内存泄露的主要缘由之一。

软引用
软引用是指非必须引用,在内存足够时是不会对其进行回收,而在内存不足的时候垃圾回收器会将其回收并释放内存。java中软引用对应着SoftReference类,若是想要一个对象被软引用,只须要将其做为参数传入SoftReference类的构造方法中就好了。

软引用主要用于用户实现相似缓存的功能,在内存足够的状况下直接经过软引用取值,无需从繁忙的真实来源查询数据,提高速度;当内存不足时,自动删除这部分缓存数据,从真实来源查询这些数据。软引用能够和一个引用队列(ReferenceQueue)联合使用,若是软引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的队列引用中。

弱引用
弱引用相对于软引用,被弱引用的对象拥有更短的内存时间(生命周期)。垃圾回收器一旦发现了被弱引用的对象,就会回收它的内存,无论当前内存空间是否是足够。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定很快发现那些弱引用的对象。对应java中的WeakReference类,使用方法与软引用基本相同,弱引用能够和一个引用队列(ReferenceQueue)联合使用,若是弱引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的队列引用中。

弱引用主要应用于监控对象是否被垃圾回收器标记为即将回收的垃圾,能够经过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记

虚引用
顾名思义:形同虚设,虚引用并不会决定对象的生命周期,若是一个对象仅有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。程序也不能经过虚引用访问被引用的对象。
虚引用主要用来跟踪垃圾回收器回收的活动,虚引用与弱应用,软引用的区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象内存以前,把这个虚引用加入到与之关联的队列中。


11.使用过哪些JVM调试工具,主要分析哪些内容?

监视JVM
jps
jstat
jstatd
Jmc

故障排除
jcmd
jinfo
jhat
jmap
jsadebugd
jstack

jps (JavaVirtual Machine Process Status Tool): 虚拟机进程情况工具
命令格式:jps [options] [hostid]
-q : 抑制类名的输出,JAR文件名和传递给main方法的参数,仅生成本地JVM标识符列表。
-m: 显示传递给该main方法的参数。输出多是null嵌入式JVM。
-l : 显示应用程序main类的完整包名或应用程序的JAR文件的完整路径名。
-v : 显示传递给JVM的参数。
-V : 抑制类名的输出,JAR文件名和传递给main方法的参数,仅生成本地JVM标识符的列表。
-Joption : 传递option给JVM,其中的选项是optionsJava应用程序启动器的参考页面中描述的选项之一。例如,-J-Xms48m将启动内存设置为48 MB。
样例:

jstat(Java Virtual Machine (JVM) statistics):监视Java虚拟机(JVM)统计信息。
命令格式:jstat [ option vmid [interval[s|ms] [count] ]
Interval:间隔时间 count:次数
class:显示类加载器行为的统计信息。
compiler:显示有关Java HotSpot VM即时编译器行为的统计信息。
gc:显示垃圾回收堆行为的统计信息。
gccapacity:显示有关世代及其对应空间容量的统计数据。
gccause:显示有关垃圾回收统计信息(相同-gcutil)的摘要,其中包含最后和当前(适用时)垃圾收集事件的缘由。
gcnew:显示新一代行为的统计信息。
gcnewcapacity:显示有关新一代及其相应空间大小的统计信息。
gcold:显示旧版本和Metaspace统计信息的统计信息。
gcoldcapacity:显示有关旧一代大小的统计信息。
gcmetacapacity:显示有关元空间大小的统计信息。
gcutil:显示有关垃圾收集统计信息的摘要。
printcompilation:显示Java HotSpot VM编译方法统计信息。

jinfo(Configuration Info for Java):生成配置信息。
命令格式:jinfo [ option ] pid
-flag 名称 : 打印指定命令行标志的名称和值。
-flag [+ | - ]名称 : 启用或禁用指定的布尔命令行标志。
-flag name = value : 将指定的命令行标志设置为指定的值。
-flags : 打印传递给JVM的命令行标志。
-sysprops : 将Java系统属性打印为名。

jmap(Memory Map for Java):内存映射工具[生成堆转储快照]
命令格式:jinfo [ option ] vmid
-dump:[live,] format = b,file = filename : 以hprof二进制格式转储Java堆filename。live子选项说明是否之dump出存活的对象。
-finalizerinfo : 打印有关正在等待最终肯定的对象的信息(linux)。
-heap :显示java堆详细信息,如使用哪一种回收器、参数配置、分代情况等(linux)。
-histo [:live] : 显示堆中对象统计信息,包括类、实例数量、合计容量。
-clstats : 打印Java堆的类加载器智能统计。对于每一个类加载器,它的名称,它的活动程度,地址,父类加载器以及它加载的类的数量和大小。
-F : -dump或 -histo选项不响应时,该选项强制生成dump快照(不支持live)。

jhat(JVM Heap Analysis Tool):虚拟机堆转储快照分析工具
命令格式:jhat [ options ] 堆转储文件

jstack(Stack Trace for Java):Java堆栈跟踪工具命令格式:jstack [ options ] pid-F : jstack[ -l] pid不响应时强制堆栈转储。-l : 打印有关锁的其余信息,例如所java.util.concurrent拥有的同步器列表。-m : 打印混合模式堆栈跟踪,其中包含Java和本机C / C ++框架。

相关文章
相关标签/搜索