强引用:java
用的最广。咱们平时写代码时,new一个Object存放在堆内存,而后用一个引用指向它,这就是强引用。面试
若是一个对象具备强引用,那垃圾回收器毫不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。算法
软引用:缓存
若是一个对象只具备软引用,则内存空间足够时,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。(备注:若是内存不足,随时有可能被回收。)服务器
只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存。数据结构
弱引用:多线程
弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。并发
每次执行GC的时候,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。jvm
虚引用:性能
“虚引用”顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。
Java程序在运行时,须要在内存中的分配空间。为了提升运算效率,就对数据进行了不一样空间的划分,由于每一片区域都有特定的处理数据方式和内存管理方式。
一、程序计数器:(线程私有)
每一个线程拥有一个程序计数器,在线程建立时建立,
指向下一条指令的地址
执行本地方法时,其值为undefined
二、虚拟机栈:(线程私有)
每一个方法被调用的时候都会建立一个栈帧,用于存储局部变量表、操做栈、动态连接、方法出口等信息。局部变量表存放的是:编译期可知的基本数据类型、对象引用类型。
每一个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
在Java虚拟机规范中,对这个区域规定了两种异常状况:
(1)若是线程请求的栈深度太深,超出了虚拟机所容许的深度,就会出现StackOverFlowError(好比无限递归。由于每一层栈帧都占用必定空间,而 Xss 规定了栈的最大空间,超出这个值就会报错)
(2)虚拟机栈能够动态扩展,若是扩展到没法申请足够的内存空间,会出现OOM
三、本地方法栈:
(1)本地方法栈与java虚拟机栈做用很是相似,其区别是:java虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈则为虚拟机执使用到的Native方法服务。
(2)Java虚拟机没有对本地方法栈的使用和数据结构作强制规定,Sun HotSpot虚拟机就把java虚拟机栈和本地方法栈合二为一。
(3)本地方法栈也会抛出StackOverFlowError和OutOfMemoryError。
四、堆:即堆内存(线程共享)
(1)堆是java虚拟机所管理的内存区域中最大的一块,java堆是被全部线程共享的内存区域,在java虚拟机启动时建立,堆内存的惟一目的就是存放对象实例几乎全部的对象实例都在堆内存分配。
(2)堆是GC管理的主要区域,从垃圾回收的角度看,因为如今的垃圾收集器都是采用的分代收集算法,所以java堆还能够初步细分为新生代和老年代。
(3)Java虚拟机规定,堆能够处于物理上不连续的内存空间中,只要逻辑上连续的便可。在实现上既能够是固定的,也能够是可动态扩展的。若是在堆内存没有完成实例分配,而且堆大小也没法扩展,就会抛出OutOfMemoryError异常。
五、方法区:(线程共享)
(1)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
(2)Sun HotSpot虚拟机把方法区叫作永久代(Permanent Generation),方法区中最终要的部分是运行时常量池。
可达的/可触及的:
Java对象被建立后,若是被一个或多个变量引用,那就是可达的。即从根节点能够触及到这个对象。
其实就是从根节点扫描,只要这个对象在引用链中,那就是可触及的。
可恢复的:
Java对象再也不被任何变量引用就进入了可恢复状态。
在回收该对象以前,该对象的finalize()方法进行资源清理。若是在finalize()方法中从新让变量引用该对象,则该对象再次变为可达状态,不然该对象进入不可达状态
不可达的:
Java对象不被任何变量引用,且系统在调用对象的finalize()方法后依然没有使该对象变成可达状态(该对象依然没有被变量引用),那么该对象将变成不可达状态。
当Java对象处于不可达状态时,系统才会真正回收该对象所占有的资源。
一、引用计数算法:
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候刻计数器为0的对象就是不可能再被使用的。
可是,主流的java虚拟机并无选用引用计数算法来管理内存,其中最主要的缘由是:它很难解决对象之间相互循环引用的问题。
二、根搜索算法:(jvm采用的算法)
设立若干种根对象,当任何一个根对象(GC Root)到某一个对象均不可达时,则认为这个对象是能够被回收的。
一、标记-清除算法:
标记阶段:先经过根节点,标记全部从根节点开始的可达对象。所以,未被标记的对象就是未被引用的垃圾对象;
清除阶段:清除全部未被标记的对象。
二、复制算法:(新生代的GC)
将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,而后清除正在使用的内存块中的全部对象。
三、标记-整理算法:(老年代的GC)
标记阶段:先经过根节点,标记全部从根节点开始的可达对象。所以,未被标记的对象就是未被引用的垃圾对象
整理阶段:将将全部的存活对象压缩到内存的一端;以后,清理边界外全部的空间
四、分代收集算法:
存活率低:少许对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少许存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只须要付出少许存活对象的复制成本就能够完成GC。
存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,由于对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。
一、Serial收集器:(串行收集器)
这个收集器是一个单线程的收集器,但它的单线程的意义并不只仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工做,更重要的是在它进行垃圾收集时,必须暂停其余全部的工做线程(Stop-The-World:将用户正常工做的线程所有暂停掉),直到它收集结束。
二、ParNew收集器:Serial收集器的多线程版本(使用多条线程进行GC)
ParNew收集器是Serial收集器的多线程版本。
它是运行在server模式下的首选新生代收集器,除了Serial收集器外,目前只有它能与CMS收集器配合工做。CMS收集器是一个被认为具备划时代意义的并发收集器,所以若是有一个垃圾收集器能和它一块儿搭配使用让其更加完美,那这个收集器必然也是一个不可或缺的部分了。
三、ParNew Scanvenge收集器
相似ParNew,但更加关注吞吐量。目标是:达到一个可控制吞吐量的收集器。
停顿时间和吞吐量不可能同时调优。咱们一方买但愿停顿时间少,另一方面但愿吞吐量高,其实这是矛盾的。由于:在GC的时候,垃圾回收的工做总量是不变的,若是将停顿时间减小,那频率就会提升;既然频率提升了,说明就会频繁的进行GC,那吞吐量就会减小,性能就会下降。
吞吐量:CPU用于用户代码的时间/CPU总消耗时间的比值,即=运行用户代码的时间/(运行用户代码时间+垃圾收集时间)。好比,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
四、G1收集器:
是当今收集器发展的最前言成果之一,直到jdk1.7,sun公司才认为它达到了足够成熟的商用程度。
五、CMS收集器:(老年代收集器)
CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。适合应用在互联网站或者B/S系统的服务器上,这类应用尤为重视服务器的响应速度,但愿系统停顿时间最短。
Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各类类的实例对象。
在 Java 中,堆被划分红两个不一样的区域:年轻代 ( Young )、老年代 ( Tenured)。年轻代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。 这样划分的目的是为了使 JVM 可以更好的管理堆内存中的对象,包括内存的分配以及回收。
1.年轻代
年轻代用来存放新近建立的对象,尺寸随堆大小的增大和减少而相应的变化,默认值是保持为堆大小的1/15,能够经过 -Xmn 参数设置年轻代为固定大小,也能够经过 -XX:NewRatio 来设置年轻代与年老代的大小比例,年青代的特色是对象更新速度快,在短期内产生大量的“死亡对象”。
年轻代的特色是产生大量的死亡对象,而且要是产生连续可用的空间, 因此使用复制清除算法和并行收集器进行垃圾回收.对年轻代的垃圾回收称做初级回收 (minor gc)。
2.老年代
Full GC 是发生在老年代的垃圾收集动做,所采用的是标记-清除算法。
现实的生活中,老年代的人一般会比新生代的人 “早死”。堆内存中的老年代(Old)不一样于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。所以,Full GC 发生的次数不会有 Minor GC 那么频繁,而且作一次 Full GC 要比进行一次 Minor GC 的时间更长。 另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后须要为较大的对象分配内存空间时,若没法找到足够的连续的内存空间,就会提早触发一次 GC 的收集动做。
3.永久代
永久代是Hotspot虚拟机特有的概念,是方法区的一种实现,别的JVM都没有这个东西。在Java 8中,永久代被完全移除,取而代之的是另外一块与堆不相连的本地内存——元空间。
永久代或者“Perm Gen”包含了JVM须要的应用元数据,这些元数据描述了在应用里使用的类和方法。注意,永久代不是Java堆内存的一部分。永久代存放JVM运行时使用的类。永久代一样包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终造成能够被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
注意:子类初始化问题:知足主动调用,即父类访问子类中的静态变量、方法,子类才会初始化;不然仅父类初始化。
注意:访问类或接口的静态变量(特例:若是是用static final修饰的常量,那就不会对类进行显式初始化。static final 修改的变量则会作显式初始化)
上面的运行效果显示,因为c是final static修饰的静态常量,因此根本就没有调用静态代码块里面的内容,也就是说,没有对这个类进行显式初始化。