本文主要介绍JVM和GC解析
本文较长,分为上下篇(可收藏,勿吃尘)
若有须要,能够参考
若有帮助,不忘 点赞 ❥java
一文理清JVM和GC下篇
web
其中方法区和堆被JVM中多个
线程共享
,好比类的静态常量就被存放在方法区,供类对象之间共享,虚拟机栈、本地方法栈、程序计数器是每一个线程独立
拥有的,不会与其余线程共享。因此Java在经过new建立一个类对象实例的时候,一方面会在虚拟机栈中建立一个对该对象的引用,另外一方面会在堆上建立类对象的实例,而后将对象引用指向该对象的实例。对象引用存放在每个方法对应的栈帧中。算法
虚拟机栈:
虚拟机栈中执行每一个方法的时候,都会建立一个栈帧用于存储局部变量表,操做数栈,动态连接,方法出口等信息。本地方法栈:
与虚拟机栈发挥的做用类似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每一个本地方法的时候,都会建立一个栈帧用于存储局部变量表,操做数栈,动态连接,方法出口等信息。方法区:
它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,方法区在JDK1.7版本及以前称为永久代,从JDK1.8以后永久代被移除。堆:
堆是Java对象的存储区域,任何new字段分配的Java对象实例和数组,都被分配在了堆上,Java堆可以使用 - Xms / - Xmx 进行内存控制,从JDK1.7版本以后,运行时常量池从方法区移到了堆上。程序计数器:
指示Java虚拟机下一条须要执行的字节码指令。从图中咱们能够看出JAVA8的JVM 用元空间取代了永久代
数组
JVM的实现通常不采用这种方式缓存
缺点:
1. 每次对对象赋值时均要维护引用计数器,且计数器自己也有必定的消耗;
2. 较难处理循环引用;网络
Java 堆从GC的角度能够细分为:新生代(Eden区、From Survivor区 和 To Survivor区)和 老年代。
特色:
复制算法不会产生内存碎片,但会占用空间。用于新生代。app
算法分红标记和清除两个阶段,先标记出要回收的对象,而后统一回收这些。
特色:
不会占用额外空间,但会扫描两次,耗时,容易产生碎片,用于老年代jvm
优势:
没有内存碎片,能够利用bump
缺点:
须要移动对象的成本,用于老年代
原理:post
- 标记:与标记清除同样
![]()
---
2.压缩:再次扫描,并往一段滑动存活对象
![]()
---
Java中,引用和对象是有关联的。若是要操做对象则必须用引用进行。
所以,很显然的一个方法就是经过引用计数来判断一个对象是否能够回收。简单来讲就是给对象添加一个引用计数器。每当有一个地方引用它,计数器的值加1,每当有一个引用失效时,计数器的值减1。
任什么时候刻计数器值为0的对象就是不可能再被使用的,那么这个对象就是可回收对象。
缺点: 很难解决对象之间相互循环引用的问题性能
所谓“GC roots” 或者说tracing GC 的 "根集合" 就是一组必须活跃的引用。
基本思路就是经过一系列名为“GC Roots” 的对象做为起始点,从这个被称为GC Roots的对象开始向下搜索,如GC Roots没有任何引用链相连是,则说明此对象不可用。也即给定一个集合的引用做为根出发,经过引用关系
标配参数
X参数
XX参数
两个经典参数
-XX:+PrintFlagsInitial
查看默认初始值-XX:+PrintFlagsFinal
查看修改更新
经典案例设置:
-Xms128m -Xmx4096m -Xss1024k -XX:Metaspacesize=512m -XX:+PrintCommandLineFlags -XX:PrintGCDetails -XX:UseSerialGC
-Xms
初始化大小内存,默认为物理内存1/64
等价于 -XX:InitialHeapSize
-Xmx
最大分配内存,默认为物理内存1/4
等价于 -XX:MaxHeapSize
-Xss
设置单个线程的大小,通常默认为5112K~1024K
等价于 -XX:ThreadStackSize
-Xmn
设置年轻代大小
-XX:MetaspaceSize
设置元空间大小
-XX:+PrintGCdetails
输出详细的GC收集日志信息
-XX:SurvivorRatio
设置新生代中eden和S0/S1空间的比例
默认:
-XX:SurvivorRatio=8 --> Eden:S0:S1=8:1:1
修改:
-XX:SurvivorRatio=4 --> Eden:S0:S1=4:1:1
SurvivorRatio值就是设置eden区的比例占多少,S0/S1相同
-XX:NewRatio
设置年轻代与老年代在堆结构的占比
默认
-XX:NewRatio=2 新生代占1,老年代占2,年轻代占整个堆的1/3
修改
-XX:NewRatio=4 新生代占1,老年代占4,年轻代占整个堆的1/5
NewRatio值就是设置老年代的占比,剩下的1给新生代
-XX:MaxTenuringThreshold
设置垃圾最大年龄
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。若是设置为0的话,则年轻代对象不通过Survivor区,直接进入老年代。对于老年代比较多的应用,能够提升效率。若是将此值设置为一个较大值,则年轻代对象会在Survivor区进行屡次复制,这样能够增长对象在年轻代的存活时间,增长年轻代被回收的概论。
强引用
public static void main(String[] args) {
Object o1 = new Object(); //默认为强引用
Object o2 = o1; //引用赋值
o1 = null; //置空 让垃圾收集
System.gc();
System.out.println(o1); // null
System.out.println(o2); // java.lang.Object@1540e19d
}
复制代码
软引用
public static void main(String[] args) {
Object o1 = new Object();
SoftReference softReference = new SoftReference(o1);
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}
复制代码
弱引用
public static void main(String[] args) {
Object o1 = new Object();
WeakReference weakReference = new WeakReference(o1);
o1 = null;
System.gc();
System.out.println(o1); //null
System.out.println(weakReference.get()); //null
}
复制代码
虚引用
public static void main(String[] args) {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue);
System.out.println(o1); //java.lang.Object@1540e19d
System.out.println(phantomReference.get()); //null
System.out.println(referenceQueue.poll()); //null
}
复制代码
扩展
public static void main(String[] args) {
WeakHashMap<Integer,String> weakHashMap = new WeakHashMap<>();
Integer key = new Integer(1);
weakHashMap.put(key,"测试1");
System.out.println(weakHashMap); //{1=测试1}
key=null;
System.out.println(weakHashMap); //{1=测试1}
System.gc();
System.out.println(weakHashMap+"\t"+weakHashMap.size()); //{} 0
}
复制代码
本文较长,能看到这里的都是好样的,成长之路学无止境
今天的你多努力一点,明天的你就能少说一句求人的话!好久好久以前,有个传说,听说:
看完不赞,都是坏蛋