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

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下加载自己创建的类

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++入门(3)】函数重载、引用

一、函数重载 1、函数重载概念 函数重载是指在同一作用域中&#xff0c;具有不同形参列表&#xff08;参数的 个数 或 类型 或类型顺序 不同&#xff09;的同名函数。 C语言中不允许同名函数的存在&#xff0c;如果一个程序中有两个函数的函数名完全相同&#xff0c;就会报错…

C#(C Sharp)学习笔记_多态【十九】

前言 个人觉得多态在面向对象编程中还比较重要的&#xff0c;而且不容易理解。也是学了一个下午&#xff0c;才把笔记写得相对比较完善&#xff0c;但仍欠缺一些内容。慢慢来吧…… 什么是多态&#xff1f; 基本概念 在编程语言和类型论中&#xff0c;多态&#xff08;Poly…

2024最新版Node.js下载安装及环境配置教程(非常详细)

一、进入官网地址下载安装包 官网&#xff1a;Node.js — Run JavaScript Everywhere 其他版本下载&#xff1a;Node.js — Download Node.js (nodejs.org) 选择对应你系统的Node.js版本 二、安装程序 &#xff08;1&#xff09;下载完成后&#xff0c;双击安装包&#xf…

OpenGL Super Bible 7th-Primitives, Pipelines, and Pixels图元、渲染管线与像素

简介 本文的原版为《OpenGL Super Bible 7th》,是同事给我的,翻译是原文+译文的形势。文章不属于机器直译,原因在于语言不存在一一对应的关系,我将尽可能的按照中国人看起来舒服的方式来翻译这些段子,如果段子让你感到身心愉悦,那还劳烦点个关注,追个更。如果我没有及时…

从0进入微服务需要了解的基础知识

文章目录 系统架构演化过程为什么要了解系统架构的演化过程技术发展认知技术选型与创新 演变过程单体架构分层-分布式集群微服务 分布式\集群\微服务 微服务中的核心要素-拆分原则项目拆分与复杂度微服务的拆分维度有哪些小结 微服务中的核心要素服务化进行拆分后一定是微服务&…

MFC扩展库BCGControlBar Pro v35.0新版亮点:重新设计的工具栏编辑器等

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v35.0已全新发布了&#xff0c;这个版本改进类Visual Studio 2022的视觉主题、增强对多个…

ChatGPT付费创作系统V3.0.2独立版 WEB+H5+小程序端 (H5端界面美化+Pika视频作品广场+SunoAI 文生歌)系统部署教程

播播资源GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序&#xff0c;是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。当前全民热议ChatGPT&#xff0c;流量超级大&#xff0c;引流不要太简单&#xff01;一键下单即可拥有自己的GPT&#xff01;无限…

MinIO Enterprise Cache:实现超性能的分布式 DRAM 缓存

随着计算世界的发展和 DRAM 价格的暴跌&#xff0c;我们发现服务器配置通常配备 500GB 或更多的 DRAM。当您处理大型部署时&#xff0c;即使是那些具有超高密度 NVMe 驱动器的部署&#xff0c;这些服务器上的服务器数量乘以 DRAM 也会迅速增加&#xff0c;通常达到几 TB。该 DR…

【Intel CVPR 2024】通过图像扩散模型生成高质量360度场景,只需要一个语言模型

在当前人工智能取得突破性进展的时代&#xff0c;从单一输入图像生成全景场景仍是一项关键挑战。大多数现有方法都使用基于扩散的迭代或同步多视角内绘。然而&#xff0c;由于缺乏全局场景布局先验&#xff0c;导致输出结果存在重复对象&#xff08;如卧室中的多张床&#xff0…

Android网络性能监控方案 android线上性能监测

1 Handler消息机制 这里我不会完整的从Handler源码来分析Android的消息体系&#xff0c;而是从Handler自身的特性引申出线上卡顿监控的策略方案。 1.1 方案确认 首先当我们启动一个App的时候&#xff0c;是由AMS通知zygote进程fork出主进程&#xff0c;其中主进程的入口就是Ac…

.Net OpenCVSharp生成灰度图和二值图

文章目录 前言一、灰度图二、二值图 前言 使用OpenCVSharp生成图片的灰度图和二值图 .Net 8.0版本&#xff0c;依赖OpenCvSharp4和OpenCvSharp4.runtime.win组件。 原图&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、灰度图 /// &…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 内存访问热度分析(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

Proteus8.13安装及使用

Proteus安装包下载地址 具体安装方法如下&#xff1a; 退出所有杀毒软件,右键以管理员身份运行 如果缺插件安装插件然后点击安装 如果遇到这种需要勾选的都勾选 安装插件完成 安装过程: 安装完成后桌面会自动出现图标 注意这个安装包是免破解的, 安装好以后可以直接使用 打…

竞赛选题 LSTM的预测算法 - 股票预测 天气预测 房价预测

0 简介 今天学长向大家介绍LSTM基础 基于LSTM的预测算法 - 股票预测 天气预测 房价预测 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/postgraduate 1 基于 Ke…

React+TS前台项目实战(十一)-- 全局常用组件提示语可复制Link组件封装

文章目录 前言HighLightLink组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇讲的这个组件&#xff0c;是一个用于高亮显示文本并添加可选的跳转链接&#xff0c;提示文本&#xff0c;复制文本的 React 组件 HighLightLink组件 1. 功能分析 &#x…

SmartEDA、Multisim、Proteus大比拼:电路设计王者之争?

在电路设计领域&#xff0c;SmartEDA、Multisim和Proteus无疑是三款备受瞩目的软件工具。它们各自拥有独特的功能和优势&#xff0c;但在这场电路设计王者的竞争中&#xff0c;谁才是真正的领跑者&#xff1f;让我们深入探究这三款软件的异同&#xff0c;揭示它们各自的魅力所在…

【ComfyUI】Stable Diffusion 3 加Controlnet

基于 instantX-research/diffusers_sd3_control: &#x1f917; Diffusers: State-of-the-art diffusion models for image and audio generation in PyTorch and FLAX. (github.com) 和 ZHO-ZHO-ZHO/ComfyUI-SD3-Medium-CN-Diffusers: ComfyUI SD3-Medium ControlNet&#…

JRebel-JVMTI [FATAL] Couldn‘t write to C:\Users\中文用户名-完美解决

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 热部署下载参考博客解决第一步第二步第三步&#xff1a;第四步&#xff1a; 热部署下载 下载后启动报错&#xff1a;JRebel-JVMTI [FATAL] Couldn’t write to C:\…

WebSocket实现消息实时通知

参考文档&#xff1a;万字长文&#xff0c;一篇吃透WebSocket&#xff1a;概念、原理、易错常识、动手实践、WebSocket 教程 1 背景 有一个需求&#xff0c;需要实现实时通信的功能&#xff0c;如果有新消息&#xff0c;后端会主动发送请求告知前端有新消息&#xff0c;需要前…

git Fork或者git clone克隆别人的项目到自己的仓库如何保持原仓库同步

一、问题描述 有时候我们会clone别人的项目到自己的仓库来进行二次开发,开发好之后提交到自己的仓库&#xff0c;如有原仓库有更新了,可以选择性的进行同步 二、解决方法 这里以ruoyi-vue-pro得前端项目来进行演示&#xff0c;创建一个目录&#xff0c;在目录下随便创建一个文…