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

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

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

相关文章

常用算法及参考算法 (1)累加 (2)累乘 (3)素数 (4)最大公约数 (5)最值问题 (6)迭代法

常用算法及参考算法 &#xff08;1&#xff09;累加 &#xff08;2&#xff09;累乘 &#xff08;3&#xff09;素数 &#xff08;4&#xff09;最大公约数 &#xff08;5&#xff09;最值问题 &#xff08;6&#xff09;迭代法 1. 累加 #include <stdio.h>int main() {…

上海亚商投顾:沪指缩量调整 PCB概念股持续爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 大小指数昨日走势分化&#xff0c;沪指全天震荡调整&#xff0c;创业板指午后涨超1%。消费电子板块全天强势&a…

【ARM】MDK Debug模式下Disassembly窗口介绍

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 主要了解Disassembly窗口中包含的内容&#xff0c;和如何利用Disassembly中的内容了解程序的存储和调用情况。 2、 问题场景 对于Disassembly窗口中具体包含的内容不了解&#xff0c;无法合理地应用Disassembly窗口…

Docker的基本操作 及 容器与外部机互相通讯(持续更新中)

Docker入门&#xff1a; Docker 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)docker入门&#xff0c;这一篇就够了。-CSDN博客Docker 容器使用 | 菜鸟教程 (runoob.com)Docker自定义网络和运行时指定IP_docker run ip-CSDN博客 基本命令 链接&#xff1a;docker入门&#…

希尔排序-C语言版本

前言 从希尔开始&#xff0c;排序的速度就开始上升了&#xff0c;这里的排序开始上一个难度了&#xff0c;当然难一点的排序其实也不是很难&#xff0c;当你对于插入排序了解的足够深入的时候&#xff0c;你会发现其实希尔就是插入的异形&#xff0c;但是本质上还是一样的 希尔…

openresty(Nginx) 301重定向域名 http访问强制使用https

1 访问http 2 修改配置访问 server {listen 80;server_name example.cn;return 301 https://$server_name$request_uri;access_log /data/logs/czgzzfjgsup_access.log access;error_log /data/logs/czgzzfjg_error.log error;#location / {root /usr/local/open…

Mac 开发vscode常用命令

1 打开vscode settting配置 commandshiftp 输入&#xff1a;Open User Setting 2

CV预测:快速使用DenseNet神经网络

AI预测相关目录 AI预测流程&#xff0c;包括ETL、算法策略、算法模型、模型评估、可视化等相关内容 最好有基础的python算法预测经验 EEMD策略及踩坑VMD-CNN-LSTM时序预测对双向LSTM等模型添加自注意力机制K折叠交叉验证optuna超参数优化框架多任务学习-模型融合策略Transform…

小规模自建 Elasticsearch 的部署及优化

本文将详细介绍如何在 CentOS 7 操作系统上部署并优化 Elasticsearch 5.3.0,以承载千万级后端服务的数据采集。要使用Elasticsearch至少需要三台独立的服务器,本文所用服务器配置为4核8G的ECS云服务器,其中一台作为 master + data 节点、一台作为 client + data 节点、最后一…

QT——MySQL数据库联用

一、ODBC 1、ODBC简介 ODBC全称为Open Database Connectivity,是一种用于数据库操作的标准接口。要使用ODBC,首先需要安装相应的ODBC驱动程序,然后在系统中配置ODBC数据源。接着,可以通过编程语言(如C++、Java等)或者数据库工具(如SQL Server Management Studio)来连…

Visual Studio Code的安装与配置

Visual Studio Code&#xff08;简称 VS Code&#xff09;是 Microsoft 在2015年4月30日 Build 开发者大会上正式宣布一个运行于 Mac OS X、Windows和 Linux 之上的&#xff0c;针对于编写现代 Web 和云应用的跨平台源代码编辑器&#xff0c;可在桌面上运行&#xff0c;并且可用…

Unity API学习之资源的动态加载

资源的动态加载 在实际游戏开发的更新换代中&#xff0c;随着开发的软件不断更新&#xff0c;我们在脚本中需要拖拽赋值的变量会变空&#xff0c;而要想重新拖拽又太花费时间&#xff0c;因此我们就需要用到Resources.Load<文件类型>("文件名")函数来在一开始…

大模型基础——从零实现一个Transformer(5)

大模型基础——从零实现一个Transformer(1)-CSDN博客 大模型基础——从零实现一个Transformer(2)-CSDN博客 大模型基础——从零实现一个Transformer(3)-CSDN博客 大模型基础——从零实现一个Transformer(4)-CSDN博客 一、前言 上一篇文章已经把Encoder模块和Decoder模块都已…

深度學習筆記12-優化器對比(Tensorflow)

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習紀錄博客&#x1f356; 原作者&#xff1a;K同学啊 | 接輔導、項目定制 一、我的環境 電腦系統&#xff1a;Windows 10 顯卡&#xff1a;NVIDIA Quadro P620 語言環境&#xff1a;Python 3.7.0 開發工具&…

基于GTX的64B66B编码IP生成(高速收发器二十)

点击进入高速收发器系列文章导航界面 1、配置GTX IP 相关参数 前文讲解了64B66B编码解码原理&#xff0c;以及GTX IP实现64B66B编解码的相关信号组成&#xff0c;本文生成64B66B编码的GTX IP。 首先如下图所示&#xff0c;需要对GTX共享逻辑进行设置&#xff0c;为了便于扩展&a…

【开发工具】git服务器端安装部署+客户端配置

自己安装一个轻量级的git服务端&#xff0c;仅仅作为代码维护&#xff0c;尤其适合个人代码管理。毕竟代码的版本管理是很有必要的。 这里把git服务端部署在centos系统里&#xff0c;部署完成后可以通过命令行推拉代码&#xff0c;进行版本和用户管理。 一、服务端安装配置 …

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

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

windows环境下,怎么查看本机的IP、MAC地址和端口占用情况

1.输入ipconfig,按回车。即查看了IP地址&#xff0c;子码掩码&#xff0c;网关信息。 2.输入ipconfig/all,按回车。即查看了包含IP地址&#xff0c;子码掩码&#xff0c;网关信息以及MAC地址 3.我们有时在启动应用程序的时候提示端口被占用&#xff0c;如何知道谁占有了我们需要…

Vue57-组件的自定义事件_解绑

给谁绑的自定义事件&#xff0c;就找谁去触发&#xff1b;给谁绑的自定义事件&#xff0c;就找谁去解绑&#xff1b; 一、解绑自定义事件 1-1、解绑一个自定义事件 到student.vue组件中去解绑。 1-2、解绑多个自定义事件 使用数组来解绑多个。 1-3、解绑所有的自定义事件 二、…

Android Studio无法连接夜神模拟器的解决方案

一、AS检测不到夜神模拟器 1、问题描述 在按照教程【如何安装和使用Android夜神模拟器】进入夜神的bin目录&#xff0c;输入连接命令回车后&#xff0c;终端显示的already connected to 127.0.0.1:62001&#xff0c;但是AS的Running Devices并没有显示夜神模拟器。 2、解决方…