JVM原理

JVM原理

JVM:JAVA二进制字节码的运行环境
优点:
1.一次编写到处运行
2.自动内存管理,垃圾自动回收

基本概念
程序计数器:Java程序变为JVM指令后,记录下一次执行指令
栈帧:每个方法运行时需要的内存
stackoverflowError:栈溢出
outofmemoryError :Java heap space:堆溢出
outofmemoryError : PermGen space:永久代溢出

JVM与JDK的关系

在这里插入图片描述

常见JVM

在这里插入图片描述

程序寄存器(PC)

1.程序寄存器使用的是CPU的寄存器
2.用来记录下次执行的jvm指令的地址
3.线程私有
4.JVM中唯一不会内存溢出的区域
在这里插入图片描述

栈(stack)

栈:线程运行时所需要的内存
栈帧:每个方法运行时需要的内存
活动栈帧:当前正在执行的方法所占用的内存
指定栈的大小: -Xss size linux默认1024KB

在这里插入图片描述
在这里插入图片描述
线程栈溢出诊断(linux环境下)
jstack 进程号

堆(Heap)

通过new关键字创建的对象使用堆内存
特点:
1.线程共享
2.有垃圾回收回收机制
堆指定大小: -Xmx size

在这里插入图片描述

堆内存诊断

jsp和jmap

1.jps:查询进程id
2.jmap -heap 进程id:查看运行时一瞬间堆内存状况

F:\projecttest\springbootdome2>jps
10784 Launcher
1872
10868 UserController
10536
7196 Launcher
9756 Jps

F:\projecttest\springbootdome2>jmap -heap 10868
Attaching to process ID 10868, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2122317824 (2024.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 707264512 (674.5MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 112721920 (107.5MB)
   used     = 81065136 (77.30973815917969MB)
   free     = 31656784 (30.190261840820312MB)
   71.91603549691133% used
From Space:
   capacity = 1572864 (1.5MB)
   used     = 65536 (0.0625MB)
   free     = 1507328 (1.4375MB)
   4.166666666666667% used
To Space:
   capacity = 1572864 (1.5MB)
   used     = 0 (0.0MB)
   free     = 1572864 (1.5MB)
   0.0% used
PS Old Generation
   capacity = 89653248 (85.5MB)
   used     = 773000 (0.7371902465820312MB)
   free     = 88880248 (84.76280975341797MB)
   0.8622108147158261% used

1764 interned Strings occupying 158080 bytes.

jconsole图形化界面
F:\projecttest\springbootdome2>jconsole

在这里插入图片描述

jvisualvm
F:\projecttest\springbootdome2>jvisualvm

在这里插入图片描述
在这里插入图片描述
抓取当前快照
在这里插入图片描述
查询堆中内存占用排名前20的对象,然后点击“是”
在这里插入图片描述
查看占用堆内存最大的对象
在这里插入图片描述
找到占用内存最大的属性
在这里插入图片描述
在这里插入图片描述

MAT(Eclipse中使用)

jps 查看进程
jmap -dump:format=b,file=XXX.bin 进程id 生成dump文件file=生成的文件名

jprofiler (IDEA中使用)

在这里插入图片描述
下载插件启动:https://www.ej-technologies.com/download/jprofiler/files
在这里插入图片描述
双击“jprofiler_windows-x64_10_1_1.exe”程序安装

在这里插入图片描述

注册码(任选其一)
  L-GXdJph7lkG-1CvkTwHlvk#741

  L-KMGcqlMxTo-Fkd1ultFTt#3810

  A-TgIjrDbn41-5w05ktrJFf#18128

  A-3rk5EoAR9t-vdItuCJtVi#2898

  S-DJMhaqMnvW-36Sb5jcGYO#31259

  L-qOQRsFcEcF-LqVM1lqxQm#1437

  A-3VIC9ISIit-SkVGccmWta#689

  L-dpoWt86zUZ-tzJNLsXY75#4229

  S-yYk9XkHyyY-3GVAtuBBkR#1104

  S-QH9BAugD8L-EZ2KbOTiIL#31225

安装完成后再IDEA中添加启动文件
在这里插入图片描述
设置堆溢出时生成jprofiler文件

-Xms1m -Xmx8m   -XX:+HeapDumpOnOutOfMemoryError

在这里插入图片描述
运行程序当出现OutOfMemoryError报错就会生成jprofiler文件
在这里插入图片描述
生成文件就在src的同级目录下在这里插入图片描述
在这里插入图片描述
打开文件
在这里插入图片描述

方法区

在这里插入图片描述
1.6版本方法区概念上属于永久代是堆的一部分
在这里插入图片描述
1.8版本以后方法区属于元空间占用的是本地内存

常量池

常量池就是一张表,虚拟机指令根据这张表找到要执行的类名,方法名,参数类型等

反编译查看常量池中的内容

F:\projecttest\springbootdome2\target\classes\com\example\demo\controller>javap -v UserController.class

在这里插入图片描述

运行时常量池

当类被加载时,他的常量池信息就会被放到运行时常量池中,并且将符号地址变为真实地址

StringTable(串池)

java1.7以前串池是放在常量池中
Java1.7以后串池是放在堆内存中

StringTable垃圾回收
-Xmx10m  堆内存大小
-XX:+PrintStringTableStatistics   打印串池日志
-XX:+PrintGCDetails  -verbose:gc  打印垃圾回收日志
 public static void main(String[] args) {
        try {
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

在这里插入图片描述
向串池中加入100条数据

 public static void main(String[] args) {
        try {
            for(int i=0;i<100;i++){
                 String.valueOf(i).intern();
            }
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

在这里插入图片描述
向串池中加入20000条数据

 public static void main(String[] args) {
        try {
            for(int i=0;i<20000;i++){
                 String.valueOf(i).intern();
            }
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

在这里插入图片描述
垃圾回收日志多了一条
在这里插入图片描述
结论:StringTable会被垃圾回收

StringTable调优

设置StringTable的大小

-XX:StringTableSize=1009
测试一

使用IDEA查看串池中的数量
在这里插入图片描述

  public static void main(String[] args) {
        System.out.println("1");  //串池中的数量2503
        System.out.println("2");  //串池中的数量2504
        System.out.println("3");  //串池中的数量2505
        System.out.println("1");  //串池中的数量2505
        System.out.println("2");  //串池中的数量2505
        System.out.println("3");  //串池中的数量2505
    }

结论:串池中如果已经存在字符串,就会直接使用串池中的字符串,不会生成新的字符串

测试二
  public static void main(String[] args) {
        //在串池创建常量["a","b"]
        //s=StringBuilder.append("a").append("a").toString();
        //堆  new String("a"),new String("b"),new String("ab")
        String s=new String("a")+new String("b");
        //将new String("ab")放入串池中,如果“ab”不存在就会放入,如果存在就不会放入,返回串池中的对象s2
        String s2 = s.intern();
        //此时串池中的常量["a","b","ab"],所以"ab"不会再创建,x就是串池中已经存在的"ab"
        String x="ab";
        //true  
        System.out.println(s2==x);
        //true  
        System.out.println(s==x);
    }
  public static void main(String[] args) {
        String x="ab";
        //串池中的常量["a","b","ab"]
        //s=StringBuilder.append("a").append("a").toString();
        //堆  new String("a"),new String("b"),new String("ab")
        String s=new String("a")+new String("b");
        //将new String("ab")放入串池中,如果“ab”不存在就会放入,如果存在就不会放入,返回串池中的对象s2
        //此时串池中的常量["a","b","ab"],所以s不会被放入串池中
        String s2 = s.intern();
        //true
        System.out.println(s2==x);
        //false
        System.out.println(s==x);
    }

结论:串池中如果已经存在字符串,就会直接使用串池中的字符串,不会生成新的字符串
在这里插入图片描述

直接内存(DirectBuffer)

1.直接内存不属于JVM,属于系统内存
2.读写性能好,但是回收成本高
3.不受JVM管理
ByteBuffer.allocateDirect(int byte_length) :使用直接内存

原理

普通缓存:java不能直接访问系统内存,只能先将系统的缓存数据读到java的缓存区才可以使用
在这里插入图片描述
直接内存:java直接访问内存
在这里插入图片描述

垃圾回收

是否可以被回收

引用计数法

当一个对象被引用一次,计数器就加1,当某个变量不在引用就减1,当引用数为0时就可以被垃圾回收器回收
缺点:当两个对象互相引用就无法被垃圾回收器回收

可达性分析算法

如果被根对象直接或间接引用就不能被回收,如果没有被根对象直接或间接引用就可以被回收

四种引用

在这里插入图片描述

垃圾回收算法

标记清除算法

在这里插入图片描述
优点:速度快
缺点:空间不连续,产生内存碎片

标记整理算法

在这里插入图片描述
优点:节省内存
缺点:整理内存会消耗资源,速度慢

复制算法

将内存划分from和to两块区域
在这里插入图片描述
标记没有引用的对象
在这里插入图片描述
将存在引用的对象复制到to区,然后删除from区标记的对象
在这里插入图片描述
将to区变为from区,将from区变为to区
在这里插入图片描述
优点:不会产生碎片
缺点:占用双倍空间

分代回收

在这里插入图片描述
在这里插入图片描述
大对象直接存入老年代

垃圾回收器

在这里插入图片描述

串行(SerialGC)

在这里插入图片描述

吞吐量优先(ParallelGC)

在这里插入图片描述

响应时间优先(CMS)

在这里插入图片描述

Garbage First(G1)

jdk9默认垃圾回收器
jdk8使用G1需要添加启动参数:-XX:+UseG1GC
特点:
1.年轻代、老年代是独立且连续的内存块;
2.年轻代收集使用单eden、双survivor进行复制算法;
3.老年代收集必须扫描整个老年代区域;
4.都是以尽可能少而块地执行GC为设计原则。

回收过程

向伊甸园区存入数据
在这里插入图片描述
新生代垃圾回收后
在这里插入图片描述
幸存区对象标记数量达到16次后
在这里插入图片描述
当老年代的内存占堆内存的45%初始标记
在这里插入图片描述
混合收集
在这里插入图片描述

FULL GC

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桀骜浮沉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值