复制->清空->互换java
MaxTenuringThreshold
决定,默认参数是15),最终若是仍是存活,就存入老年代。垃圾:内存中已经再也不被使用到的空间就是垃圾。linux
可达性分析算法:基本思路是经过一系列名为 GC Roots 的对象做为起始点,从这些对象开始向下搜索,若是一个对象到 GC Roots 没有任何引用链相连,说明此对象不可用。ios
GC Roots 是一组必须活跃的引用。算法
能够做为 GC Roots 的对象的有:缓存
1. 标配参数:在 jdk 各个版本之间保持稳定bash
- java -version
- java -help
- java -showversion
复制代码
2. X参数服务器
-Xint:解释执行
-Xcomp: 第一次使用就编译成本地代码
-Xmixed: 默认,先编译后执行
复制代码
3. XX参数网络
Boolean 类型多线程
-XX:+/- 属性
首先使用 jps -l
查看当前运行进程 id并发
而后使用 jinfo -flag PrintGCDetails 进程id
来查看
若是开启,结果应该是-XX:+PrintGCDetails
,不然-XX:-PrintGCDetails
KV类型
-XX:key=value
jinfo -flag MetaspaceSize 进程id
来查看。-Xms
等价于-XX:InitialHeapSize
,-Xmx
等价于-XX:MaxHeapSize
java -XX:+PrintFlagsInital
查看jvm初始参数java -XX:+PrintFlagsFinal -version
查看修改更新的参数。参数中使用 =
说明是未修改的参数,使用:=
说明是人为或jvm修改过的参数。java -XX:+PrintCommandLineFlags
JDK 1.8 以后永久代取消了,由元空间取代。新生代由 Eden + SurvivorFrom + SurvivorTo 组成,大小用-Xmn
设置。JVM 堆由新生代和老年代共同组成,由-Xms
和-Xmx
设置大小。元空间再也不属于堆的一部分。
元空间和永久代的区别在于:永久代使用 JVM 的堆内存,元空间不在虚拟机中而是使用本机物理内存。
默认状况下,元空间的大小仅受本地内存限制,类的元数据放入 native memory,字符串池和类的静态变量放入 java 堆中,这样能够加载多少类的元数据就再也不由 MaxPermSize 控制,而由系统的实际可用空间来控制。
-Xms
-XX:InitialHeapSize
-Xmx
-XX:MaxHeapSize
-Xss
-XX:ThreadStackSize
-Xmn
-XX:MetaspaceSize
-XX:+PrintGCDetails
[GC [PSYoungGen: 2048K -> 496K(2560K)] 2048K->777K(9728k)]
复制代码
[GC [PSYoungGen:
表示GC类型2048K
表示 YoungGC 前新生代内存占用496K
表示 YoungGC 后新生代内存占用(2560K)
表示新生代总大小2048K
表示 YoungGC 前 JVM 堆内存占用777K
表示 YoungGC 后 JVM 堆内存占用9728K
表示 JVM 堆总大小[Full GC (System) [PSYoungGen:3408K->0K(296688k)] [PSOldGen:0K->3363K(682688K)]3408K->3363K(981376K)[Metaspace:10638K->10638K(131072K)]]
复制代码
能够看到 Full GC 中,分别有 新生代、老年代、堆总内存以及元空间各自的GC前、GC后以及总大小。
-XX:SurvivorRatio
-XX:NewRatio
-XX:MaxTenuringThreshold
SoftReference, WeakReference 和 PhantomReference 三个类都继承自 Reference 类,而 Reference 类又是 Object 类的子类。
当内存不足,JVM 开始垃圾回收,可是强引用的对象,即便出现了 OOM 也不会对该对象进行回收。把一个对象赋给一个引用变量,这个引用变量就是一个强引用,只要还有强引用指向一个对象,该对象就处于可达状态,不会被 JVM 回收。
对于只有软引用的对象来讲,系统充足时不会被回收,系统内存不足时会被回收。软引用一般用在对内存敏感的程序中,好比高速缓存就用到软引用。
适用场景(缓存):假设有一个应用大量读取本地图片,每次读取都会IO影响性能,一次性所有加载到内存可能会内存溢出。
可使用 HashMap 保存图片的路径和图片对象关联的软引用之间的映射关系,内存不足时, JVM 会自动回收这些缓存图片对象所占用的空间,有效避免了OOM的问题。
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
复制代码
只要GC一运行,无论JVM的内存空间是否足够,都会回收该对象占用的内存。
相比于 HashMap, WeakHashMap 中的元素,当 key 被回收后,Map 中相应的键值对将再也不存在。
public class WeakHashMapDemo {
public static void main(String[] args) {
myHashMap();
System.out.println("======================");
myWeakHashMap();
}
private static void myWeakHashMap() {
WeakHashMap<Integer,String> map=new WeakHashMap<>();
Integer k=new Integer(1);
String v="str";
map.put(k,v);
System.out.println(map);
k=null;
System.out.println(map);
System.gc();
System.out.println(map);
}
private static void myHashMap() {
HashMap<Integer,String> map=new HashMap<>();
Integer k=new Integer(1);
String v="str";
map.put(k,v);
System.out.println(map);
k=null;
System.out.println(map);
System.gc();
System.out.println(map);
}
}
复制代码
运行结果
{1=str}
{1=str}
{1=str}
======================
{1=str}
{1=str}
{}
复制代码
public class ReferenceQueueDemo {
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
WeakReference<Object> weakReference=new WeakReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println();
o1=null;
System.gc();
Thread.sleep(1000);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
}
}
复制代码
java.lang.Object@1b6d3586
java.lang.Object@1b6d3586
null
null
null
java.lang.ref.WeakReference@4554617c
复制代码
弱引用、软引用、虚引用在 gc 以前,会被放到引用队列中。
虚引用顾名思义就是形同虚设,它并不会决定对象的声明周期。若是一个对象仅仅持有虚引用,就和没有引用同样,随时可能被回收。虚引用不能单独使用,也不能经过它访问对象,必须和引用队列联合使用。
虚引用的主要做用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被 finalize 之后,作某些事情的机制。其意义在于说明一个对象已经进入 finalization 阶段,能够被回收,用来实现比 finalization 机制更加灵活的回收操做。
设置虚引用的惟一目的,就是在这个对象被回收的时候收到一个系统通知或者后序添加进一步的处理。
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
PhantomReference<Object> phantomReference=new PhantomReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println();
o1=null;
System.gc();
Thread.sleep(1000);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
}
}
复制代码
运行结果:
java.lang.Object@1b6d3586
null
null
null
null
java.lang.ref.PhantomReference@4554617c
复制代码
能够看到,虚引用的 get() 返回值永远是 null。
在建立引用的时候能够指定关联的队列,当 GC 释放对象内存的时候,会将引用加入到引用队列。若是程序发现某个虚引用已经被加入到引用队列,那么就能够在所引用的对象的内存被回收以前采起必要行动,至关于一种通知机制。经过这种方式,JVM容许咱们在对象被销毁后,作一些咱们想作的事情。
当递归调用中,栈的调用深度过深,致使栈溢出错误。 继承关系以下:
Throwable -> Error -> VirtualMachineError -> StackoverflowError
复制代码
当对象过多,或者存在大对象等状况下,致使堆内容超过堆的最大尺寸,致使堆溢出。
GC回收时间过程会抛出此类异常。过长的定义是超过 98% 的时间用来作 GC 而且回收了不到 2% 的堆内存。连续屡次 GC 都只回收了不到 2% 的极端状况下才会抛出。
假如不抛出 GC overhead limit 错误会发生的状况: GC 清理出的内存很快再次被填满,破事 GC 再次执行,造成恶性循环,CPU 使用率一直是 100%,可是 GC 却没有任何成果。
public class GCOverheadDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
while (true) {
list.add(String.valueOf(++i).intern());
}
}
}
复制代码
写 NIO 程序常用 ByteBuffer 来读取或者写入数据。它可使用 Native 函数库直接分配堆外内存,而后经过一个存储在 Java 堆里边的 DirectByteBuffer 对象做为这块内存的引用进行操做。这样在一些场景能显著提升性能,由于避免了在 Java 堆和 Native 堆中来回复制数据。
ByteBuffer.allocate(capability)
是分配JVM堆内存,属于GC管辖范围,因为须要拷贝因此速度相对较慢。
ByteBuffer.allocateDirect(capability)
是分配 OS 本地内存,不属于 GC 管辖范围,因为不须要内存拷贝因此速度相对较快。
若是不断分配本地内存,堆内存不多使用,那么 JVM 就不须要执行 GC,DirectByteBuffer 对象就不会被回收,致使堆内存充足,可是本地内存已经被用光了。
在高并发请求服务器时,会出现该异常。
致使的缘由:
解决办法:
MetaSpace 是方法区在 HotSpot 中的实现,它并不在虚拟机内存中而是使用本地内存,也就是类的元数据被存储在 MetaSpace 的 native memory 中。
其中存放了:虚拟机加载的类的信息,常量池,静态变量和即时编译的代码。
串行垃圾回收器,为单线程环境设计且只使用一个线程进行垃圾回收,会暂停全部的用户线程,因此不适合服务器环境。
过程就是:程序运行->单GC线程->程序运行
并行垃圾回收器,多个垃圾收集线程并行工做,此时用户线程是暂停的,适用于科学计算、大数据处理等弱交互场景。
过程:程序运行->多GC线程并行运行->程序运行
并发垃圾回收器,用户线程和GC线程同时执行,不须要停顿用户线程,适用于对响应时间有要求的场景(强交互)。
过程:初始标记(暂停,单线程)->并发标记(GC线程和用户线程同时执行)->最终标记(暂停,多GC线程)->清除
G1垃圾回收器将堆内存分割成不一样的区域(Region)而后并发的对其进行垃圾回收。
使用java -XX:+PrintCommandLineFlags -version
查看默认的垃圾回收器。
在 java8 中,默认的是 -XX:+UseParallelGC
。
java 的 gc 类型有如下几种:
在 jvm 中的 7 种垃圾回收器中,Serial Old 已经被废弃,因此在 gc 的源代码中,有如上六种。
在新生代的有:
在老年代的有:
两者均可以使用的是 G1。
DefNew: Default New Generation
Tenured: Old
ParNew: Parallel New Generation
PSYoungGen: Parallel Scavenge
ParOldGen: Parallel Old Generation
复制代码
一个单线程的收集器,在GC的时候,必须暂停其余全部的工做线程直到 GC 结束。
JVM参数:-XX:+UseSerialGC
开启后,新生代使用 Serial 老年代使用 Serial Old,新生代和老年代都使用串行回收收集器,新生代使用复制算法,老年代使用标记-整理算法。
GC 日志中,新生代使用DefNew
,老年代使用Tenured
。
使用多线程进行垃圾回收,垃圾收集时,会 STW 暂停其余线程直到 GC 结束。
JVM 参数:-XX:+UseParNewGC
。启用后,新生代使用 ParNew ,老年代使用 Serial Old,新生代使用复制算法,老年代使用标记-整理算法。 要注意的是, ParNew + Serial Old 已经再也不被推荐。
GC 日志中,新生代使用ParNew
,老年代使用Tenured
。
Parallel Scavenge 收集器相似于 ParNew 也是一个新生代垃圾收集器,使用复制算法,是并行的多线程的垃圾收集器,是吞吐量优先的收集器。
它重点关注的是吞吐量(运行用户代码时间/(运行用户代码时间+垃圾收集时间))。高吞吐量意味着高效利用CPU的时间,适用于在后台运算而不须要太多交互的任务。
Parallel Scavenge 和 ParNew 收集器的一个重要区别是自适应调节策略。自适应调节策略是指虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。
JVM 参数:-XX:+UseParallelGC
,与使用-XX:+UseParallelOldGC
效果相同,这两个收集器默认搭配使用,因此会互相激活。开启后,新生代使用复制算法,老年代使用标记-整理算法。
启用该收集器后,GC 日志中,新生代输出PSYoungGen
,老年代输出ParOldGen
。
在 Java 8 中,该收集器为默认的收集器。
Parallel Old 收集器是 Parallel Scavenge 的老年代版本,使用多线程的标记-整理算法,在 JDK 1.6开始提供。
在 JDK 1.6 以前,新生代使用 Parallel Scavenge 只能搭配老年代的 Serial Old,只能保证新生代的吞吐量优先。
在 JDK 1.8 及之后,默认搭配为 Parallel Scavenge + Parallel Old ,保证了总体的吞吐量优先。
JVM 参数:-XX:+UseParallelOldGC
,开始后,新生代使用 Parallel Scavenge,老年代使用 Parallel Old。
启用该收集器后,GC 日志中,新生代输出PSYoungGen
,老年代输出ParOldGen
。
CMS(Councurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器,适合应用在互联网或B/S系统的服务器上,这类应用中是服务器的相应速度,但愿系统停顿时间最短。CMS 适合堆内存大、CPU 核数多的服务器端应用。
JVM 参数:-XX:+UseConcMarkSweepGC
,开启该参数后会自动开启-XX:+UseParNewGC
。使用 ParNew (新生代) + CMS + Serail Old 的收集器组合,其中 Serial Old 做为 CMS 出错的后备收集器。
因为耗时最长的并发标记和并发清除过程当中,GC 线程和用户一块儿并发工做,因此整体上看 CMS 的 GC 和用户线程一块儿并发执行。
优势:并发收集、停顿低。
缺点:
Serial Old 是 Serial 收集器的老年代版本,是个单线程收集器,使用标记-整理算法,是 Client 默认的老年代收集器。
在 Server 模式下:
-XX:+UseSerailGC
-XX:+UseParallelGC
或-XX:+UseParallelOldGC
-XX:+UseConcMarkSweepGC
参数 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC |
SerialGC | 复制 | SerailOldGC | 标记整理 |
-XX:+UseParNewGC |
ParNew | 复制 | SerailOldGC | 标记整理 |
-XX:+UseParallelGC /-XX:+UseParallelOldGC |
Parallel Scavenge | 复制 | Parallel Old | 标记整理 |
-XX:+UseConcMarkSweepGC |
ParNew | 复制 | CMS+Serial Old | 标记清除 |
garbage-first heap+metaspace
G1 是一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽量的知足垃圾收集暂停时间的要求。而且具备以下特性:
G1 的设计目标是取代 CMS 收集器,同 CMS 相比:
G1 的主要改变是 Eden, Survivor 和 Tenured 等内存区域再也不是连续的,而是变成了一个个大小同样的 region。
每一个 Region 大小范围在 1MB-32MB,最多能够设置 2048 个区域(默认也是2048),所以能支持的最大内存为 64 GB。
G1 仍然属于分代收集器。这些 Region 的一部分包含新生代,新生代的垃圾(Eden)收集采用 STW 的方式,将存活对象拷贝到老年代或者 Survivor 空间。Region 的另外一部分属于老年代, G1 经过将对象从一个区域复制到另一个区域,完成清理工做,所以不会有 CMS 内存碎片问题的存在了。
在 G1 中,还有一种特殊的区域,称为 Humongous 区域,若是一个对象占用的空间超过了 Region 的 50% 以上,就被认为是巨型对象,会直接分配在老年代。G1 划分了一个 Humongous 区,用来存放巨型对象。若是一个 Humongous Region 装不下一个巨型对象,G1会寻找连续的 Humongous Region 来存储,为了能找到连续的 H 区,有时候不得不启动 Full GC。
Young GC: 针对 Eden 区进行收集, Eden 区耗尽后被处罚,主要是小区域收集 + 造成连续的内存块,避免内存碎片。
top
命令查看CPU 占用率、内存占用率以及第一行的 load averageuptime
命令,是系统性能命令的精简版vmstat -n 2 3
表明每2秒采样一次,一共采样3次mpstat -P ALL 2
查看全部 CPU 核信息pidstat -u 1 -p pid
查看每一个进程使用 CPU 的用量分解信息free -m
来查看内存状况,单位为 mbpidstat -p pid -r 采样间隔秒数
df -h
iostat -xdk 2 3
pidstat -d 采样间隔秒 -p pid
ifstat 采样间隔秒
top
命令找出 CPU 占比最高的进程ps -ef
或者 jps
进一步定位该进程信息ps -mp pid -o THREAD,tid,time
jstack 进程id | grep tid -A60