大话jvm-那些事

本文详细探讨了JVM内存模型,包括运行时数据区的组成,如程序计数器、堆、栈、方法区和本地方法栈。讲解了垃圾收集器的工作原理和不同算法,如引用计数、复制、标记清除和标记整理。同时,介绍了如何通过JVM参数进行调优,如设置线程栈大小、MetaspaceSize、SurvivorRatio、NewRatio和MaxTenuringThreshold等。此外,文章还讨论了强引用、软引用、弱引用和虚引用的特性,以及常见的OOM错误和G1垃圾收集器的应用。
摘要由CSDN通过智能技术生成

jvm 又称java 虚拟机。一个庞大的体系,只有慢慢认识了它,才能玩的更好。
本文本着what,why,how 的原则展开,
1、JVM内存模型是啥?每个分区组成是啥?
2、运行时数据区有啥?堆内存结构 特点是啥?
3、gc 3种收集器的原理是啥?为啥这么搞?
4、如何使用gc 调优工具?how
首先从大局上给jvm一个定位哈
在这里插入图片描述
从图中可看出,jvm操作的是操作系统,与操作系统交互的是执行引擎,执行引擎负责解释命令,提交给操作系统。
接着认识下jvm内部组成。
在这里插入图片描述
中间区域即运行时数据区由5部分组成,程序计数器、堆、栈、方法区、本地方法区
注意粉红色区域是gc起作用的区域、说白了就是要进行垃圾回收的哈~
通常来说,我们部署的应用是jar,war 都是.class文件,那么一个.class文件如何加载到jvm中呢,请看下图
在这里插入图片描述
上图这个东东,让一个class文件—loader–Class对象模板构建到内存中。如下:

Java栈(线程私有-gc不优化)
	线程私有 thread,copy 主内存,引用,
	栈由一系列帧组成(因此Java栈也叫做帧栈)
	帧保存一个方法的局部变量、操作数栈、常量池指针
	每一次方法调用创建一个帧,并压栈
	存啥-(method 表,局部变量表,操作数栈 指向运行时常量池引用 方法返回地址....入栈 8种类型+引用变量+方法 copy+入栈 出栈操作~)

pc寄存器、指针-指向方法区中字节指令
	线程私有-
Java堆(线程共享-gc管辖 95%,红色)
	和程序开发密切相关
	应用系统对象都保存在Java堆中
	所有线程共享Java堆
	对分代GC来说,堆也是分代的
	GC的主要工作区间  gc,new,
	新生区,养老区,永久区(元空间-不变信息 堆外空间-同方法区)
		新生区-细分3个
		永久区-常量池 jdk 1.7,jdk 1.8,但jdk 1.6在方法区
			
方法区(共享-gc管辖 5%,红色)
	方法区是啥?接口、规范、定义
	保存装载的类信息 
	static,构造函数,class name,package name
	静态变量+常量 final+类信息(构造方法+接口定义)+运行时常量池 string a="ccc"
	字段,方法信息 filed name,method,inferce,
	方法字节码
	通常和永久区(Perm)关联在一起
	
栈、堆、方法区交互
	栈存-堆对象的地址,堆的对象来自方法区模板Class(类信息)
	
内存模型
	每一个线程有一个工作内存和主存独立
	工作内存存放主存中变量的值的拷贝
	每一个操作都是原子的,即执行期间不会被中断
	对于普通变量,一个线程中更新的值,不能马上反应在其他变量中
	如果需要在其他线程中立即可见,需要使用 volatile 关键字,强制从主内存取~

对于堆得认识我们需要进一步分析…
在这里插入图片描述
在这里插入图片描述
堆内部又分三块,新生,老年,元空间。对于新生代又分分三块,我们通过gc日志认识一下

首先我有个问题判断对象为垃圾对象,也就是此对象已经废了、jvm回收垃圾时,如何确定垃圾-gcRoots

初步猜测,每一个对象都通过引用来使用。那么没有引用不就废弃了。
其实猜的八九不离十,有两种策略
	1、引用计数法:对象引用一次加1,为0时 回收   缺点:性能差,无法解决a,b互相引用
	2、可达性分析:用到了gcroot、对象和gcroot之间是否有链路 无链路就回收  说白了-gcroot就是对象的引用区。
	那么gcRoot里有哪些对象呢?---(局部变量表,方法区类属性引用,方法区常量引用,栈引用)
	
public class GCRoot {
	
    private static  GCRoot1 root2;
    private static  final GCRoo2 root3;
    public static void mm() {
        GCRoot root1=new GCRoot();
        System.gc();
    }
}
// 分析:方法区的static -final, 和方法mm中的局部变量都可作为gcroot对象

在这里插入图片描述
在这里插入图片描述
图为gcRoot和对象之间的关系
在这里插入图片描述
确定是垃圾了,采取何种算法回收垃圾 ?
1、引用计数 2、复制 3、标记清除 4、标记整理,其中2作用于新生代,3 4作用于老年代
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

tomcat需要在catalina.sh加参数

JAVA_OPTS="-XX:+PrintHeapAtGC -XX:+PrintGCDetails -server -Dfile.encoding=UTF-8 -Xms1024m -Xmx3096m -XX:PermSize=512m -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.security.egd=file:/dev/./urandom -Djavax.net.debug=all"

了解了啥是垃圾,如何回收垃圾、那么给你一个正在运行的java程序、你如何查到某个jvm参数是否开启,值是啥 ?jvm参数配置-从大方向上分为3种。标配参数,x参数,xx参数。重点了解xx参数。其他2种都是不变的。
1、boolen类型 举例: -XX:+PrintGCDetails +开启,-关闭
2、key-value类型: -XX:ParallelGCThreads=20 key=value类型
如何查看系统默认jvm参数 ?工作常用jvm参数 ?

[root@vm10-0-1-157 ~] jps -l //java ps-可查看进程号对应java程序
3137 eureka-7003.jar
11874 sun.tools.jps.Jps
3114 eureka-7002.jar

细粒度查看,查看某个参数大小
jinfo -flag PrintGCDetails\MetaspaceSize 进程号

// java ps -查看机器上个的java进程
[root@vm10-0-1-157 ~]# jps -l
3137 eureka-7003.jar
3114 eureka-7002.jar
26381 sun.tools.jps.Jps
[root@vm10-0-1-157 ~]# 
// 查看某一个java进程-jvm 默认值
[root@vm10-0-1-157 ~]# jinfo -flags 3137
Attaching to process ID 3137, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=482344960 -XX:MaxNewSize=160759808 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
Command line:  
[root@vm10-0-1-157 ~]# 
[root@vm10-0-1-157 ~]# jinfo -flag InitialHeapSize 3137
-XX:InitialHeapSize=31457280
 

有个问题,参数都是 -XX: key=value.boolean这种值 ,那么xms xmx 如何看 ?
在这里插入图片描述

// 1、查看jvm 所有默认参数-
[root@vm10-0-1-157 ~]# java -XX:+PrintFlagsInitial
[Global flags]
    uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
    uintx AdaptiveSizePausePolicy                   = 0                                   {product}
    uintx AdaptiveSizePolicyCollectionCostMargin    = 50                                  {product}
    uintx AdaptiveSizePolicyInitializingSteps       = 20                                  {product}
    uintx AdaptiveSizePolicyOutputInterval          = 0                                   {product}
    
2、查看jvm修改的变量值:
java -XX:+PrintFlagsFinal -version
// := 修改的值
运行时实时修改
	java -XX:+PrintFlagsFinal -XX:MetaspaceSize=512M Demo.java

3、java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=30141312 -XX:MaxHeapSize=482260992 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

System.out.println(Runtime.getRuntime().maxMemory());
System.out.println(Runtime.getRuntime().totalMemory());
System.out.println(Runtime.getRuntime().availableProcessors());

工作常用参数:

-XX:+UseParallelGC 并行垃圾回收器
-XX:NewSize=41943040 
-XX:OldSize=83886080 

-XX:InitialHeapSize=125829120  ==-Xms
-XX:MaxHeapSize=2011168768     ==-Xmx
-XX:MaxNewSize=670040064       ==-Xmn

-XX:ThreadStackSize=1024k      ==Xss

-XX:MetaspaceSize=512m
-XX:+PrintGCDetails

-XX:SurvivorRatio=8
-XX:NewRatio=2
-XX:MaxTenuringThreshold=15

-Xms10m -Xmn10m -XX:+PrintGCDetails

栈内存
1、[root@vm10-0-1-157 ~]# jinfo -flag ThreadStackSize 3137
-XX:ThreadStackSize=1024
Xss == -XX:ThreadStackSize 单个线程栈的大小,默认512k-1024k

2、 -XX:MetaspaceSize=512m
在这里插入图片描述
3、-XX:SurvivorRatio=8 新生代3个区比例调整-
在这里插入图片描述
4、-XX:NewRatio=2 老年代占用大,避免频繁gc
在这里插入图片描述
5、-XX:MaxTenuringThreshold=15 设置垃圾的进入老年代最大年龄

谈谈强引用,软引用,弱引用,虚引用? 4种写法,加速对象 gc
在这里插入图片描述
(1)、强引用-
在这里插入图片描述

	public static void test1(){
        //强引用-
        Object obj1=new Object();
        Object obj2=obj1;
        obj1=null;
        System.gc();
        System.out.println(obj2);
    }
    // 运行结果:
    java.lang.Object@47d384ee
    分析:手动触发gc,不回收-

(2)、软引用
在这里插入图片描述

设置jvm  -Xms10m -Xmn10m -XX:+PrintGCDetails
	public static void test2(){
        //软,
        Object obj1=new Object();
        SoftReference<Object> obj=new SoftReference<>(obj1);
        obj1=null;
        System.gc();//内存够用,手动 gc
        //内存不够用,自动 gc
        Byte[] bytes=new Byte[30*1024*1024];
        System.out.println(obj1);
        System.out.println(obj.get());
    }

(3)、弱引用-
在这里插入图片描述

	public void test3(){
        //弱引用-
        Object obj1=new Object();
        WeakReference<Object> obj=new WeakReference<>(obj1);
        obj1=null;
        System.gc();//内存够用,手动 gc
        //内存不够用,自动 gc
        System.out.println(obj1);
        System.out.println(obj.get());
    }
    public void test4(){
        WeakHashMap<Integer,String> map=new WeakHashMap<>();
        Integer aa=new Integer(2);
        map.put(aa,"2222");
        System.out.println(map);
        //弱引用-null、立即回收-
        aa=null;
        System.gc();
        System.out.println(map);
    }

应用场景:
在这里插入图片描述
(4)、虚引用
在这里插入图片描述

public static void test6(){
        //虚引用-
        Object obj1=new Object();
        ReferenceQueue<Object> rq=new ReferenceQueue<>();
        PhantomReference<Object> obj=new PhantomReference<>(obj1,rq);
        System.out.println(obj1);
        System.out.println(obj.get());
        System.out.println(rq.poll());//引用队列-保存
        System.out.println("======================");

        obj1=null;
        System.gc();//内存够用,手动 gc
        //内存不够用,自动 gc
        System.out.println(obj1);
        System.out.println(obj.get());
        System.out.println(rq.poll());//虚引用干掉之前-,保存-引用队列、做最后的挣扎...
    }

5、oom有哪些?
6、垃圾收集器与垃圾算法,服务器默认是?
7、G1垃圾收集器

下面是一个案例抓取的gc日志,分析—总内存大小10m,b1-b2对象大小逐渐加大。

public static void main(String[] args) {
        byte [] b1 = new byte[2 * 1024 * 1024];
        byte [] b2 = new byte[2 * 1024 * 1024];
        byte [] b3 = new byte[2 * 1024 * 1024];
        byte [] b4 = new byte[4 * 1024 * 1024];
    }
堆内存参数设置:-Xms10m -Xmn10m -XX:+PrintGCDetails
打印日志:gc 新生代,full gc 老年代
[GC (Allocation Failure) a,b,c可分配,d失败因为有6m [PSYoungGen: 5957K->1023K(8704K)] 5957K->3193K(11776K), 0.0079851 secs] [Times: user=0.01 sys=0.02, real=0.01 secs] 
 触发gc [Full GC (Ergonomics) [PSYoungGen: 1023K->443K(8704K)] [ParOldGen: 2170K->2664K(6144K)] 3193K->3107K(14848K), [Metaspace: 3222K->3222K(1056768K)], 0.0212271 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
Heap
	PSYoungGen  total 8704K, used 4737K [0x00000000ff600000, 0x00000000fff80000, 0x0000000100000000)
		eden:from:to新生区为 8:1:1, 地址偏移量~
		eden space 7680K,8m 55% used [0x00000000ff600000,0x00000000ffa314a0,0x00000000ffd80000)
		from space 1024K,1m 43% used [0x00000000ffd80000,0x00000000ffdeefa0,0x00000000ffe80000)
		to   space 1024K,1m 0% used [0x00000000ffe80000,0x00000000ffe80000,0x00000000fff80000)
ParOldGen       total 10752K, used 6760K [0x0000000088200000, 0x0000000088c80000, 0x00000000ff600000)
	object space 10752K, 62% used 
	a,b,c复制到老年代 yong gc [0x0000000088200000,0x000000008889a018,0x0000000088c80000)
	Metaspace       used 3230K, capacity 4600K, committed 4864K, reserved 1056768K
	class space    used 346K, capacity 424K, committed 512K, reserved 1048576K
通过日志分析:
此处用到了复制算法:为何复制?
a,b,c 加起来超多6m、空间不够需要向其他区域转移。
这个算法有啥特点:eden挪到to 清理eden空间-  gc时,效率高,无内存碎片、但浪费空间.

从上面可得出几个原则,
1、优先分配 eden 12%
2、大对象直接分配到老年代 -XX:PretenureSizeThreshold=6m
3、长期存活对象分配到老年代 -XX:MaxTenu ringThresho=15 达到15,进入老年代,如何算长期存活。新生代 from<->to 对象在这两个区来回挪动,每 gc一次 年龄加1,经历15次 直接 挪到老年代
在这里插入图片描述
对于gc 有专门的线程工作。我们看一道面试题

讲讲垃圾回收? 
 有关垃圾回收,我们首先要考虑两个问题。一是如何判断对象可回收,二是用什么样的方式来回收。
	首先对于前者,有引用计数法和可达性分析两种方法,它们分别……(讲讲它们的含义,优缺点)
	而对于后者,市面上主要有标记-清除,标记-整理,复制三种回收算法,它们分别……(讲讲含义,优缺点)
	结合这些算法,市面上就出现了很多垃圾收集器,例如Serial,ParNew,CMS,G1……(顺便讲讲它们的回收逻辑,优缺点)
	
	垃圾回收的话,自JDK1.8后,市面上就非常流行G1垃圾回收器了。它是不分新生代和老年代的,基本原理是……(讲讲含义,优点)
	但是垃圾的频繁回收势必会导致用户体验的下降,虽然G1已经很优秀了,作为开发者我们还是需要关注JVM的优化(话锋一转,开始走向深、讲一些具体的优化策略)
	
	
 gc对内存如何管理对象如何销毁GC(GarbageCollection)是垃圾回收机制(垃圾回收器),GC是JVM对内存(实际上就是对象)进行管理的方式
	1、Java有了GC,就不需要程序员去人工释放内存空间。GC使得Java开发人员摆脱了繁琐的内存管理工作
	2、当Java虚拟机发觉内存资源紧张的时候,就会自动地去清理无用变量所占用的内存空间
	3、程序员可以在Java程序中显式地使用System.gc()或Runtime.getRuntime().gc()来通知垃圾回收程序
	4、回收是垃圾收集器的一个动作,结果就是释放内存
	5、实际上随着分配的对象增多, GC的时间与开销将会放大。所以,JVM的内存被分为了三个主要部分:新生代,老年代和永久代

	所有新产生的对象一律都在新生代中, Eden区保存最新的对象,有两个 SurvivorSpace—— S1和 S0,三个区域的比例大致为 8:1:1
	当新生代的 Eden区满了,将触发一次 GC,我们把新生代中的 GC称为 minor garbage collections
  
  使用SerialGC的场景: 
		1、如果应用的堆大小在100MB以内。 
		2、如果应用在一个单核单线程的服务器上面,并且对应用暂停的时间无需求。 
  使用ParallelGC的场景: 
		如果需要应用在高峰期有较好的性能,但是对应用停顿时间无高要求(比如:停顿1s甚至更长)。 
  使用G1、CMS场景: 
		1、对应用的延迟有很高的要求。 
		2、如果内存大于6G请使用G1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值