• JVM,GC


    JVM调优

    JVM内存模型

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eiN5Jei4-1668999202076)(JVM优化.assets/image-20220914152329478.png)]

      • 是jvm中最大的一块内存空间,该内存被所有的线程共享,几乎所有的对象和数组都被分配到了堆内存中,
    • 程序计数器

      • 是一个很小的内存空间,主要记录每个线程执行的字节码的地址,比如,分支,循环,跳转,异常,线程等都依赖于计数器。
      • 由于Java是多线程的语言,当执行线程数量超过cpu数量时,线程之间会根据时间片轮流争夺cpu资源。如果一个线程的时间片用完了,或者其他原因导致这个线程的cpu资源被提前争夺,那么这个退出的线程就需要单独的一个程序计数器,用来记录下一条运行的指令。
    • 方法区

      • 方法区主要用来存放被虚拟机加载的类相关信息,包括类信息,运行时常量池,字符串的常量池。类信息又包括类的版本,字段,方法,接口和父类等信息。
    • 虚拟机

      • 是线程私有的内存空间,是和Java线程一起创建的,当创建一个线程时,会在虚拟机栈中申请一个线程栈,用来保存方法和局部变量,操作数栈,动态链接方法和放回地址等信息。
      • 每一个方法调用都伴随着栈帧的入栈操作,方法的放回则时栈帧的出栈操作。
    • 本地方法

      • 本地方法栈跟Java虚拟机栈的功能类似,Java虚拟机用于管理Java函数的调用,而本地方法则用于管理本地方法的调用。但本地方法并不是用Java实现的,是由c语言实现的。
    public class JVMCase {
    
        //常量
        public final static String MAN_SEX_TYPE = "man";
        //静态变量
        public static String WOMAN_SEX_TYPE = "woman";
    
        public static void main(String[] args) {
    
            Student student =new Student();
            student.setName("张三");
            student.setSexType(MAN_SEX_TYPE);
            student.setAge(18);
            //调取静态方法
            print(student);
            //调用非静态方法
            new JVMCase().print1(student);
    
        }
    
        //常规的静态方法
        private static void print(Student student){
            System.out.println("name:"+student.getName()+";sex:"+student.getSexType());
        }
    
        private void print1(Student student){
            System.out.println(student.getName()+"说:hello");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    public class Student {
        private String name;
        private String sexType;
        private int age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. jvm向操作系统申请内存,jvm第一步就是通过配置参数或者默认的配置参数向操作系统申请内存空间,根据内存大小找到具体的内存分配表,然后把内存段的起始地址和终止地址分配给jvm,接下来jvm就进行内部分配
    2. jvm获取内存空间后,会根据配置参数分配堆,栈以及方法区的内存大小
    3. class文件加载,验证,准备以及接卸,其中准备阶段会为类的静态变量分配内存,初始化为系统的初始值[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y0hVnrNS-1668999202080)(JVM优化.assets/image-20220914155951490.png)]

    4,将会进行到最后一个初始化阶段,在这个阶段中,jvm首先会执行构造器方法,编译器会将.java文件编译为.class文件,

    5,执行方法,启动main线程,执行mian方法,开始执行第一行代码,此时堆内存会创建一个student对象,对象引用student会存放在栈中

    在这里插入图片描述
    在这里插入图片描述

    垃圾回收器调优

    GC:

    • 好处
      • 开发人员无需过度关注对象的回收和释放,jvm的垃圾回收器机制可以减少不少工作量,可以完全交给jvm回收对象,减轻内存空间
    • 坏处
      • jvm的垃圾回收器的不稳定性

    垃圾回收器机制

    GC算法:

    问题???

    • 回收发生在哪
    • 对象什么时候被回收
    • 如何回收这些对象
    回收发生在哪?

    JVM的内存区域中,程序计数器虚拟机栈和本地方法栈这三个区域时线程私有的,随着线程的创建而创建销毁而销毁

    栈中的栈帧随着方法的进入退出进行入栈出栈操作。每个栈帧中分配多少内存基本时类结构确定下来的时候就已知的,因此这三个区域的内存分配和回收都具有确定性

    垃圾回收器的重点就是关注堆和方法区中的内存,堆中回收主要时对象的回收,方法区中回收的主要时废弃常量,无用的类的回收

    对象什么时候被回收掉

    一般一个对象不被引用,就代表该对象可以被回收,判断该该对象是否可以被回收:

    • 引用计数算法:

      • 通过一个对象的引用计数器来判断该对象是否被引用了,每当对象被引用,则引用计数器就会加1;每当引用失效,计数器就会减1。当对象的引用计数器的值为0时,就说明该对象不被引用,可以被回收了,
    • 可达性分析算法:

      • GC Roots是该算法的基础,GC Roots是所有对象的根对象,在jvm加载时,会创建一些普通对象引用正常对象,这些对象作为正常对象的起始点,在垃圾回收时,会从这些GC Roots开始向下搜索,当一个对象到GC Roots没有任何引用链相链时,就证明对象是不可用的,目前HotSpot虚拟机采用的是这种算法

      以上两种算法都是通过引用判断对象是否可以被回收,在jdk1.2之后,引入了扩充概念

      引入类型功能特点
      强引用(Strong Reference)被强引用关联对象永远不会被垃圾回收器回收掉
      软引用(Soft Reference)软引用关联的对象,只有当系统要发生内存溢出时,才会要去回收软引用的对象
      弱引用(Weak Reference)只被弱引用的对象,只要发生垃圾回收器时间,就会被回收
      虚引用(Phantom Reference)被虚引用关联的对象的唯一作用是能在这个对象被回收器回收时收到一个系统通知
    如何回收这些对象

    知道Java程序种对象的回收条件,怎么回收这些对象,jvm垃圾回收遵循两个特点:

    • 自动性

      • Java提供了一个系统级的线程来跟踪每一块分配出去的内存空间,当jvm处于空闲循环时,垃圾回收器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块空闲的内存块
    • 不可预期性

      • 一旦一个对象没有被引用了,该对象是否立即被回收?很难确定一个没有被引用的对象是不是被立即回收掉,可能程序接收后,这个对象还在内存中。
      • 垃圾回收线程在jvm中是自动执行的,Java程序无法强制执行,我们唯一能做的到的是System.gc()方法来”建议“执行垃圾回收器;但是否执行,什么时候执行,不可预期。

    GC算法

    JVM提供了不同的收集算法来实现这一套东西:

    回收算法类型优点缺点
    标记-清除算法(Mark-Sweep)不需要移除对象,简单高效标记-清楚过程效率低,GC产生内存碎片
    复制算法(Copying)简单高效,不会产生内存碎片内存使用率低,有可能产生频繁复制问题
    标记-整理算法(Mark-Compact)综合了前两种算法的优点仍然需要移动局部对象
    分代收集算法(Generational Collection)分区回收对于长时间存活对象的场景回收效果不明显,可能会起到反作用
  • 相关阅读:
    android: android:onClick=“@{() -> listener.onItemClick(viewModel)}“
    数据结构学习笔记
    最易/难学习的编程语言榜单出炉,C++最难学?
    软考高级系统架构设计师系列之:系统开发基础知识
    表达式求值的相关语法知识(C语言)
    微服务组件--限流框架Spring Cloud Hystrix详解
    【Unity】导航 Navigation 设置:窄桥、隧道、斜坡、台阶、坠落、跳跃
    Java封装一个根据指定的字段来获取子集的工具类
    计算机毕业设计之java+springboot基于vue的乐校园二手书交易管理系统
    【Java核心技术10】Java数组详解
  • 原文地址:https://blog.csdn.net/qq_44715376/article/details/127960234