【JVM结构、JVM参数、JVM垃圾回收】

news2024/10/7 16:22:56

JVM:Java Virtual Machine java虚拟机
虚拟机:使用软件技术模拟出与具有完整硬件系统功能、运行在一个隔离环境中的计算机系统。
JVM官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

java 一些命令

javac 将文件编译成.class文件
java 执行 .class文件,若类中没有main函数,则不能执行。如 java -cp User.jar User 运行User.jar
javap 反编译器,显示编译类中可以访问的方法和数据。 如 javap -c 打开.class

jar cvf User.jar User.class :将User.class打jar包 , 全部打包: jar -cvf xx.jar *
jar xvf 解压jar

JVM内存结构

  1. 类加载器子系统
  2. 执行引擎
  3. 本地方法库
  4. 运行时数据区:
    • 程序计数器(program counter register --pc):记录每个线程指令执行顺序、当前位置等。线程私有。

    • java虚拟机栈stack:每个线程有自己的栈,栈的创建同线程创建一起。栈有一系列栈帧组成,帧可以描述当前线程的一个方法的执行过程(每一个方法从调用直至执行完成的过程, 就对应着一个栈帧在虚拟机栈中入栈和出栈的过程),方法执行完就释放这个帧,也就释放了局部变量(基本数据类型、对象引用)的内存地址。

    • 堆heap:存放对象实例和数组,线程共享。gc的区域,新生代和老年代。

    • 方法区:已被加载的类信息(类的元数据),常量,静态变量,静态代码块,编译器编译后的代码等。
      1.8以后字符串常量池和静态变量移到堆;运行时常量池留在方法区中;删除永久代,替换为元空间(存数据的数据)。

    • 本地方法栈(native method --底层其他语言,如C++):执行本地方法需要的栈。

堆与方法区为线程共享,栈和程序计数器为线程私有。程序计数器标记了线程执行到哪儿了,线程切换了也可以回到它本来运行中断的地方;每个线程都要有个独立的程序计数器。

栈内存与堆内存

数据结构的栈 ≠ jvm栈内存;数据结构的堆 ≠ jvm堆内存。
数据结构:队列(Queue)和栈(Stack) 、链表、线性表、Map、Tree

数据结构:队列先进先出(排队)、栈先进后出

栈(Stack):执行程序用,比如:基本类型的变量和对象的引用变量。
堆(Heap):存储java中的对象和数组, 存取速度较慢。gc的区域,新生代和老年代。

栈的空间大小远远小于堆。
栈内存线程私有 (局部变量存在于栈内存);堆内存所有线程共有(成员变量存在于堆内存所以有线程安全一说)。

栈:内存溢出 StackOverFlowError、OutofMemoryError
堆:内存溢出 OutofMemoryError
方法区:也会有 OutofMemoryError

常量池
静态常量池(class文件常量池)

存储在.class文件中。

每一份class文件都有一份自己的静态常量池。类和接口名字,字段名,和其他一些在class中引用的常量,如 CONSTANT_Class_info、CONSTANT_Utf8、CONSTANT_String、CONSTANT_Integer…

静态常量池是在编译时就存入且不变更。

Class对象是存放在堆区的,不是方法区。而类的元数据(元数据并不是类的Class对象),即类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的,存在方法区。

运行时常量池

是方法区的一部分。

在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中。每个class都对应有一个运行时常量池,类在解析之后将符号引用替换成直接引用。具备动态性,运行期间也可能将新的常量放入池中。

字符串常量池

存放字符串对象的实例(堆)。在每个JVM中都只会维护一份,所有类共享。

字符串常量池保存的是“字符”的实例,供运行时常量池引用。

-XX:StringTableSize=1009 这个参数可以指定字符串常量池的容量。(JDK1.6默认为1009,JDK1.7之后默认为60013,字符串常量池底层为HashTable,合理增大常量池大小会解决Hash冲突问题,JDK1.8开始1009是可以设置的最小值)

字符串常量池、运行时常量池:关系、位置演化

JDK1.7之前,字符串常量池是运行时常量池的一部分,一起存在方法区中。
JDK1.7,字符串常量池和静态变量,移到堆中。运行时常量池还在方法区。字符串常量池不属于运行时常量池的一部分。
JDK1.8,字符串常量池和静态变量还在堆中。但运行时常量池跟随方法区一起变成元空间,进入主内存。

验证字符串常量池的位置,是在heap堆中:

ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 100000000; i++) {
    String temp = String.valueOf(i).intern();
    list.add(temp);
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	...

JVM内存模型(JMM)

Java线程之间的通信由Java内存模型控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见,线程私有内存和主内存之间的抽象关系。

JMM主要围绕可见性、原子性和有序性这三个特性而建立。具体来说:

  1. 可见性与共享变量
    在Java程序中,多个线程可能同时访问和修改共享变量。为了确保每个线程都能看到其他线程对共享变量所做的修改,Java内存模型提供了一系列规则。例如,volatile关键字可以确保变量的可见性,即当一个线程修改了一个volatile变量的值后,其他线程能够立即看到这个修改。此外,synchronized块也可以保证可见性,它确保在进入和退出synchronized块时,线程对共享变量的操作对其他线程是可见的。

  2. 有序性
    为了优化程序性能,编译器和处理器可能会对指令进行重排序。然而,这种重排序可能会导致多线程程序出现意想不到的结果。为了解决这个问题,Java内存模型定义了happens-before规则来确保多线程之间的操作顺序符合预期。简单来说,如果一个操作happens-before另一个操作,那么第一个操作的结果将对第二个操作可见。

  3. 原子性
    Java内存模型还规定了某些操作具有原子性。原子性意味着这些操作在执行过程中不会被其他线程中断。例如,对volatile变量的读写操作具有原子性。但是,需要注意的是,并非所有操作都具有原子性。对于非原子性操作,我们需要使用锁等机制来保证线程安全。

垃圾回收机制 之 如何判断对象已“死”

  1. 引用计数法
    给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;任何时刻计数器为0的对象就是不能再被使用的,即对象已“死”。
    但是, 对象之间存在相互引用, 就不会被回收. JAVA并没有采用此算法.

  2. 可达性分析算法
    通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可用。
    当一个对象 与 GC Roots 没有任何引用链项链, 证明此对象不可用.

    可作为GC Root的对象包含以下几种:

    • 虚拟机栈(栈帧中的本地变量表)中的引用
    • 方法区中静态属性
    • 方法区中常量
    • 本地方法栈中(Native方法)引用的对象

垃圾回收机制 之 垃圾回收算法

标记-清除算法

算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。后续的收集算法都是基于这种思路并对其不足加以改进而已。

  1. 标记和清除这两个过程的效率都不高
  2. 标记清除后会产生大量不连续的内存碎片,以后需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。
复制算法(新生代回收算法)

它将可用内存按容量划分为两块,每次只使用其中一块(并不是50%).当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。

现在的商用虚拟机(包括HotSpot)都是采用这种收集算法来回收新生代。

新生代中98%的对象很快就需要回收, 经过一次 复制活着的对象后, 只需要少量的空间来存放这部分活着的对象, 所以不需要1:1 ,而是将内存(新生代内存)分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor, 当回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。
默认Eden与Survivor的大小比例是8 : 2 (8:1:1) (两个Survivor是一叫From区,另一个To区)

部分对象会在From区域和To区域中复制来复制去,如此交换15次(默认),最终如果还存活,就存入老年代。

复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法, 要使用标记-整理算法

标记-整理算法(老年代回收算法)

标记过程仍与“标记-清除”过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法

不是指某种特定算法。是说针对新生代合老年代采用不同GC算法,(用不同的垃圾收集器)

  • Minor GC又称为新生代GC :
    指的是发生在新生代的垃圾收集。因为Java对象大多都具备朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。

  • Full GC 又称为老年代GC或者Major GC :
    指发生在老年代的垃圾收集。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对,在Parallel Scavenge收集器中就有直接进行Full GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。

new Object() 申请堆内存空间过程

先在 Eden 申请,可用就创建对象;不够 MinorGC(新生代-复制算法)。
GC后再次判断Eden,可用就创建, 不够再判断Survivor(from) 还有空间? 有就创建,否则判断 Old,可用就在 Old创建,不够 FullGC。 再次判断Old,可用就创建,否则GC。

垃圾收集器

语义:

  • serial 【串行】 启用的时候会暂停所有线程 只有一个线程来GC 不适用服务器环境

  • Parallel 【并行】 启用的时候会暂停所有线程 多个线程来GC

  • CMS(Concurrent Mark Sweep) 【并发】 一部分线程GC, 其他线程继续运行 (初始标记的时候也会stw,只是收集的过程中并发)

  • G1 (Garbage-First)
    在这里插入图片描述
    指定垃圾收集 :
    -XX:+UseSerialGC

      新生代 + 老年代 都是串行,即Serial + Serial old(新是复制,老是整理)
    

    -XX:+UsePerNewGC

      PerNew +  Serial old
    

    -XX:+UseParalleGC

      Parallel Sce + Parallel Old 【jdk8的默认]
    

    -XX:+UseConcMarkSweepGC

      老年代开启CMS。新生代开启PerNew
    

    -XX:+UseG1GC

      G1 同时适用 young 和 old
      适用大内存,多core。追求更短的stw的时间,在jdk1.7u4支持。在jdk1.9的默认收集器
      把整个Heap都重新分配了,划分成很多Region,2048个,每个大小范围[1M,32M] 所以Heap最大64G。
      Region: Eden、Servivor、Old、Humongous(巨大的:超过region一半大小)
      没有碎片
    

    其他的收集器:ZGC(jdk11)、Shenandoan(openJDK)

新生代和老年代大小默认比例大约1:2
新生代中eden:s0:s1 的比例也和垃圾收集器有关,通常说的是 8:1:1 其实是 SerialGC 的时候
而Java8默认Parallel GC 的话,比例大约是 4:1:1
也可以手动改比例:-XX:ServivorRatio=8

垃圾收集器如何选择

对1.8来说,CMS/G1中选一个。

  • 程序很小,单进程:串行就行
  • 多核、高吞吐:ps
  • 多核、少暂停时间:cms
  • 多核、内存连续:G1
CMS 收集器

Concurrent Mark Sweep:可并发的标记-清除 (也有碎片)

-XX:+UseConcMarkSweepGC    # 老年代开启CMS。新生代开启PerNew

CMS参考:https://plumbr.io/handbook/garbage-collection-algorithms-implementations/concurrent-mark-and-sweep

  • Phase 1: Initial Mark. (会stw)
    mark all the objects in the Old Generation that are either direct GC roots or are referenced from some live object in the Young Generation.
    会在old中标记直接由GC root 可达对象、和由young中存活对象引用的old 中的对象

  • Phase 2: Concurrent Mark. (不会stw)
    在根据上阶段标记的对象,沿着引用链标记

  • Phase 3: Concurrent Preclean.(不会stw)
    清除

  • Phase 4: Concurrent Abortable Preclean. (不会stw)

  • Phase 5: Final Remark.(会 stw)
    最后标记,因为是上一阶段的标记是并发的,标记的过程中可能同时发生新的引用变更,最后需要stw,再标记一次。

  • Phase 6: Concurrent Sweep.(不会 stw)

  • Phase 7: Concurrent Reset.

合并步骤为5步:(三次Mark,一次Sweep)

  1. Initial Mark.
  2. Concurrent Mark.
  3. Final Remark.
  4. Concurrent Sweep.
  5. Reset.
G1 收集器

Garbage-First

-XX:+UseG1GC

G1 同时适用 young 和 old
适用大内存,多core。追求更短的stw的时间,在jdk1.7u4支持。在jdk1.9的默认收集器
把整个Heap都重新分配了,划分成很多Region,2048个,每个大小范围[1M,32M] 所以Heap最大64G。
Region: Eden、Servivor、Old、Humongous(巨大的:超过region一半大小)
没有碎片

G1如何做到可预测停顿时间:通过自己管理的大小不一的region,判断可回收的空间,以及回收的开销大小,做到优先回收回收效率最高的部分region(如 region1 10M,500ms,region2 50M,100ms,优先回收region2)

GC 日志解析

日志中语义:
DefNew:新生代串行 (Serial )
Tenured:老年代串行 (Serial old)

PerNew: 新生代并行 (PerNew )

PSYoungGen:新生代并行 (Parallel Sce)
ParOldGen:老年代并行 (Parallel Old )

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

JVM参数

  • X 参数
    java -Xmixed -version 【# 开启 mixed mode 】
    java -Xint -version 【# 开启 interpreted mode (解释模式,字节码直接执行)】

  • XX 参数
    -XX:[+/-] 属性名 (启用/禁用某属性)如: -XX:+UseG1GC
    -XX:name=value (为某属性设置值) 如:-XX:MaxTenuringThreshold=10

举例:设置最大、最小堆内存

-Xmx -XX:MaxHeapSize 的简写,设置最大堆内存,默认为物理内存的1/4

-Xms1024m

-Xms -XX:InitialHeapSize 的简写,设置最小堆内存。默认为物理内存的1/64

-Xms1024k
# 或
-Xms2m
举例:设置栈的大小

-Xss -XX:ThreadStackSize 的简写。(不写单位就默认单位字节)

-Xss1024k
举例:查看这个进程是否开启了GC明细输出的开关

jinfo -flag 命令可以查看JVM参数

jinfo -flag PrintGCDetails <pid>  

如果输出:

-XX:-PrintGCDetails  # 代表没打开

所以在启动前,可以设置开启GC日志输出

-XX:+PrintGCDetails 
举例:开启类加载过程日志
-XX:+TraceClassLoading

会发现先从 jre/lib/rt.jar 加载依赖包,再从当前项目target/classes下加载自己创建的类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1834033.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Netty】nio处理acceptreadwrite事件

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Netty ⛺️稳中求进&#xff0c;晒太阳 1.处理accept 1.1客户端代码 public class Client {public static void main(String[] args) {try (Socket socket new Socket("localhost…

秋招突击——6/16——复习{整理昨天的面试资料}——新作{删除链表倒数第n个节点}

文章目录 引言复习新作删除链表倒数第N个节点题目描述个人实现参考实现 总结 引言 主管面&#xff0c;面的很凄惨&#xff0c;不过无所谓了&#xff0c;我已经尽力了。上午都在整理的面经&#xff0c;没有复习算法&#xff0c;而且这两天要弄一下论文&#xff0c;二十号就要提…

Aspice介绍——测试流程

文章目录 ASPICE简介一、V字模型的示意二、测试领域2.1 SWE.6&#xff1a;软件合格性测试过程目的过程成果基本实践&#xff08;BP&#xff09; 2.2 SYS.4:系统集成和集成测试过程目的过程成果基本实践&#xff08;BP&#xff09; 2.3 SYS.5&#xff1a;系统合格性测试过程目的…

AI早班2024.6.18

先一步知道AI未来&#xff01; 全球AI新闻速递 1.绿米 AI 智能存在传感器 FP1E开售。 2.摩尔线程 师者AI&#xff1a;完成70亿参数教育AI大模型训练测试。 3.Google 在 AI 功能推出新功能&#xff0c;需要明确说明可能出错的地方。 4.北大、快手攻克复杂视频生成难题&#…

【unity笔记】三、冰山碰撞变成碎块效果

一、模型准备 共需准备两个模型&#xff0c;一个原始模型&#xff0c;一个破碎后的模型。 破碎后的模型制作教程&#xff1a; 下载Blender 导入原始模型在添加偏好设置中添加Cell Fracture插件&#xff0c;调整模型碎裂效果。导出&#xff0c;保存到项目预制体文件夹。 二、…

性能测试项目实战

项目介绍和部署 项目背景 轻商城项目是一个现在流行的电商项目。我们需要综合评估该项目中各个关键接口的性能&#xff0c;并给出优化建议&#xff0c;以满足项目上线后的性能需要。 项目功能架构 前台商城&#xff1a;购物车、订单、支付、优惠券等 后台管理系统&#xf…

基于springboot实现药店管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现药店管理系统演示 摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;药品信息因为其管理内容繁杂&#xff0c;管理数…

wireshark使用情况与网口调试记录

wireshark使用情况与网口调试记录 前言wireshark无法获取本地数据方法一——Npcap方法二——WinPcap效果 UDP组播&#xff0c;却一直捕获到127.0.0.1总结 前言 在网口调试中&#xff0c;wireshark使用较多&#xff0c;常出现一些无法捕获或者ip获取数据不正确的情况&#xff0…

webpack工作流程

webpack工作流程 初始化参数&#xff1a;从配置文件和 Shell 语句中读取并合并参数,得出最终的配置对象用上一步得到的参数初始化 Compiler 对象加载所有配置的插件执行对象的 run 方法开始执行编译根据配置中的entry找出入口文件从入口文件出发,调用所有配置的Loader对模块进…

如何确保pcdn的稳定性?(壹)

确保PCDN的稳定性是一个重要任务&#xff0c;涉及多个方面的操作和考虑。以下是一些建议&#xff0c;帮助你确保PCDN的稳定性&#xff1a; 一&#xff0e;选择合适的服务器与硬件&#xff1a; 选择稳定可靠的服务器供应商和硬件设备&#xff0c;确保服务器具有高可用性和容错…

openGauss 6.0一主二备高可用架构部署,可靠很行

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…

支撑每秒 600 万订单无压力,SpringBoot + Disruptor 太猛了!

一、背景 工作中遇到项目使用Disruptor做消息队列,对你没看错,不是Kafka,也不是rabbitmq;Disruptor有个最大的优点就是快,还有一点它是开源的哦,下面做个简单的记录. 二、Disruptor介绍 Disruptor 是英国外汇交易公司LMAX开发的一个高性能队列&#xff0c;研发的初衷是解决内存…

软银与AI初创公司Perplexity达成合作;OpenAI或将改变治理结构,考虑成立营利性公司

&#x1f989; AI新闻 &#x1f680; 软银与AI初创公司Perplexity达成合作 摘要&#xff1a;6月17日消息&#xff0c;日本软银宣布与AI初创公司Perplexity达成战略合作&#xff0c;将于6月19日起向Softbank、Y-Mobile和LINEMO用户开放Perplexity Pro一年的免费试用申请。Perp…

Einops 张量操作快速入门

张量&#xff0c;即多维数组&#xff0c;是现代机器学习框架的支柱。操纵这些张量可能会变得冗长且难以阅读&#xff0c;尤其是在处理高维数据时。Einops 使用简洁的符号简化了这些操作。 Einops &#xff08;Einstein-Inspired Notation for operations&#xff09;&#xff…

CentOS 7 下gdb任意版本的升级

文章目录 前言查看gdb版本升级步骤 小结 前言 在做项目的过程中&#xff0c;遇到了难缠的bug&#xff0c;使用gdb调试的时候&#xff0c;bt调用堆栈看的一震头疼&#xff0c;于是就想起把gdb升级一下 当前环境&#xff1a;Centos7&#xff0c;gdb&#xff1a;7.6 稍微好看了那…

SpringCloudStream原理和深入使用

简单概述 Spring Cloud Stream是一个用于构建与共享消息传递系统连接的高度可扩展的事件驱动型微服务的框架。 应用程序通过inputs或outputs来与Spring Cloud Stream中binder对象交互&#xff0c;binder对象负责与消息中间件交互。也就是说&#xff1a;Spring Cloud Stream能…

AI工具快速制作爆火的影视视频混剪

今天给大家发一个有意思的工具&#xff0c;影视混剪大家应该都刷到过&#xff0c;像下面这种视频&#xff0c;播放量都超级高。 这种视频都是怎么做的呢&#xff1f; 现在AI工具这么多样性&#xff0c;先用 AI 写一段具有网感的对话段子&#xff0c;然后找影视剧片段混剪成一…

笑脸金融测试社招面经,期望20K

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 测试总监一面 1、问一些测试理论相关的知识。 自我介绍、质量模型 2、登录如何设计测试用例。 3、给你一个东西你会从哪些方面去考虑设计测试用…

【数据结构初阶】--- 堆的应用:topk

堆的功能&#xff1a;topk 为什么使用topk 先举个例子&#xff0c;假如说全国有十万家奶茶店&#xff0c;我现在想找到评分前十的店铺&#xff0c;现在应该怎么实现&#xff1f; 第一想法当然是排序&#xff0c;由大到小排序好&#xff0c;前十就能拿到了。这是一种方法&…

2024 年最新 Python 基于 LangChain 框架基础案例详细教程(更新中)

LangChain 框架搭建 安装 langchain pip install langchain -i https://mirrors.aliyun.com/pypi/simple/安装 langchain-openai pip install langchain-openai -i https://mirrors.aliyun.com/pypi/simple/ChatOpenAI 配置环境变量 环境变量 OPENAI_API_KEYOpenAI API 密钥…