深入理解Java虚拟机:JVM高级特性与最佳实践-总结-1

news2024/12/1 0:38:57

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-1

  • Java内存区域与内存溢出异常
    • 运行时数据区域
      • 程序计数器
      • Java虚拟机栈
      • 本地方法栈
      • Java堆
      • 方法区
    • OutOfMemoryError异常
      • Java堆溢出
  • 垃圾收集器与内存分配策略
    • 对象是否可以被回收
      • 引用计数算法
      • 可达性分析算法

Java内存区域与内存溢出异常

运行时数据区域

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如图所示:
在这里插入图片描述

程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。 因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,这类内存区域为“线程私有”的内存。

Java虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

Java堆

对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里"几乎”所有的对象实例都在这里分配内存。

Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。

方法区

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

OutOfMemoryError异常

Java堆溢出

Java堆用于储存对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么随着对象数量的增加,总容量触及最大堆的容量限制后就会产生内存溢出异常。
代码示例中限制Java堆的大小为20MB,不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDumpOnOutOf-MemoryError可以让虚拟机在出现内存溢出异常的时候Dump出当前的内存堆转储快照以便进行事后分析叫。
代码清单2-3 Java堆内存溢出异常测试

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOut OfMemoryError
 * @author zh
 */
public class HeapooM {
	static class oowobject {
	}
	
	publie statie woid main(String[] args) {
		List<OOMobject> list = new ArrayList<OOMobject>();
		while(true) {
			list.add (new OONobject ());
		}
	}
}

运行结果:

java.lang.OutofMemoryError: Java heap space
Dumping heap to java_pid3404.hprof...
Heap dump file created [22045981 bytes in 0.663 secs]

Java堆内存的OutOfMemoryError异常是实际应用中最常见的内存溢出异常情况。出现Java堆内存溢出时,异常堆栈信息"java.lang.OutOfMemoryError"会跟随进一步提示"Java heap space"

要解决这个内存区域的异常,常规的处理方法是首先通过内存映像分析工具(如Eclipse MemoryAnalyzer)对Dump出来的堆转储快照进行分析。第一步首先应确认内存中导致OOM的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(MemoryOverflow)。

如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄漏对象的类型信息以及它到GC Roots引用链的信息,一般可以比较准确地定位到这些对象创建的位置,进而找出产生内存泄漏的代码的具体位置。

如果不是内存泄漏,换句话说就是内存中的对象确实都是必须存活的,那就应当检查Java虚拟机的堆参数(-Xmx-Xms)设置,与机器的内存对比,看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运
行期的内存消耗。以上是处理Java堆内存问题的简略思路。

垃圾收集器与内存分配策略

对象是否可以被回收

引用计数算法

在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。

引用计数算法(Reference Counting)虽然占用了一些额外的内存空间来进行计数,但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。但是,在Java领域,至少主流的Java虚拟机里面都没有选用引用计数算法来管理内存,主要原因是,这个看似简单的算法有很多例外情况要考虑,必须要配合大量额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。

循环引用的代码示例如下所示:对象objAobjB都有字段instance,赋值令objA.instance = objBobjB.instance = objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为零,引用计数算法也就无法回收它们。

/**
 *testGc()方法执行后,objA和obB会不会被GC?
 *eauthor zh
 */
public class Referencecountinggc {
	public Object instance = nul1;
	private statie final int _1MB = 1024 * 1024;

	/**
	 *这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否有回收过
	 */
	private byte[] bigsize = new byte[2 * _1MB];

	public statie void testGC() {
		ReferenceCountingec objA = new ReferenceCountingGC();
		ReferenceCountingec objB = new ReferenceCountingGC();
		abjA.instance = objB:
		objB.instance = objA;
		
		objA = null;
		ob}B = null;
		
		//假设在这行发生GC,abjA和objB是否能被回收?
		System.gc();
	}
}

运行结果:

[Fu11 GC(System)[Tenured: OK->210K(10240K),0.01491428ecs] 4603K->210K(19456K),[Perm:2999K->2999K1
Heap
	def new generation total 9216K, used 82K [0x00000000055e0000,0x0000000005fe0000,0x0000000005fe0
	Eden space 8192K,    1% used (0x00000000055e0000,0x00000000055f4850,0x0000000005de0000)
	from space 1024K,  0% used (0x0000000005de0000,0x0000000005de0000,0x0000000005ee0000)
	to space 1024K,  0% used (0x0000000005ee0000,0x0000000005ee0000,0x0000000005fe0000)
	tenured generation total 10240K,used 210K[0x0000000005fe0000,0x00000000069e0000,0x00000000069e
	the space 10240K,  2% used [0x0000000005fe0000,0x0000000006014a18,0x0000000006014c00,0x00000000
	compacting perm gen total 21248K, used 3016K [0x00000000069e0000,0x0000000007ea0000,0x000000000b
	the space 21248K,14% used [0x00000000069e0000,0x0000000006cd2398,0x0000000006cd2400,0x00000000
	No shared spaces configured.

从运行结果中可以清楚看到内存回收日志中包含“4603K->210K”,意味着虚拟机并没有因为这两个对象互相引用就放弃回收它们,这也从侧面说明了Java虚拟机并不是通过引用计数算法来判断对象是否存活的。

可达性分析算法

当前主流的商用程序语言的内存管理子系统,都是通过可达性分析(Reachability Analysis)算法来判定对象是否存活的。可达性分析算法的基本思路就是通过一系列“GC Roots”的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,则证明此对象是不可能再被使用的。

如下图所示,对象object 5、object 6、object 7虽然互有关联,但是它们到GCRoots是不可达的,因此它们将会被判定为可回收的对象。
在这里插入图片描述
在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

  1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
  2. 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  3. 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  4. 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  5. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepitonOutOfMemoryError)等,还有系统类加载器。
  6. 所有被同步锁(synchronized关键字)持有的对象。
  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

除了这些固定的GC Roots集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象“临时性”地加入,共同构成完整GC Roots集合。

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

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

相关文章

力库华为机试题练习

1、两数之和 arg [2, 3, 6, 5] target 4 for i in range(len(arg)): other target - arg[i] if other in arg[i1:]: print(i, arg[i1:].index(other)i1) else: print(“输入目标数在该列表中不存在”) 2、回文数 方法一&#xff1a; class Solution: def isPalindrome(sel…

抖音小程序怎么压缩图片?教你使用抖音图片压缩助手

图片压缩是将原始图像的数据量进行减少&#xff0c;从而使其文件大小更小&#xff0c;但尽量保持原有图像质量的一种技术。通过对图片进行压缩&#xff0c;可以降低图片在传输过程中所需的带宽和存储空间&#xff0c;提高网站或应用程序的加载速度和响应速度。 此外&#xff0…

亚马逊云科技将帮助GoPlus Security,助力行业健康发展

Gartner 2022年7月发布的技术成熟度曲线分析报告显示&#xff0c;目前Web3技术已经历了第一波创新高峰期&#xff0c;正在从“创新启动阶段”向“创新泡沫阶段”过渡&#xff0c;技术体系逐步成型&#xff0c;市场热度较高&#xff0c;创业投资活跃。高速增长的背后&#xff0c…

浅谈Hutool工具类

一、Hutool简介 Hutool是一个Java工具类库&#xff0c;它封装了很多常用的Java工具类&#xff0c;如加密解密、文件操作、日期时间处理、Http客户端等。它的目标是让Java开发变得更加简单、高效。 二、Hutool的特点 高效&#xff1a;提供了很多高效的工具类和方法。 简单&…

最全的国内chatGPT大模型企业及产品整理

作者 | gongyouliu 编辑 | gongyouliu 自从去年11月30日openAI发布chatGPT以来&#xff0c;chatGPT引爆了新一轮科技革命。最近很多年都没有哪一项科技进步如chatGPT这般吸引全球的目光。除了媒体的大肆报道&#xff0c;国内外各个科技公司、科研机构、高等院校都在跟进&#x…

智能卡接口(ISO7816)

概述 智能卡接口&#xff08;7816&#xff09;是外部智能卡通过2 线交换8 位数据的串行同步通讯手段。芯片提供了2 个7816主机接口模块。 ⚫ 2路独立7816接口 ⚫ 具备卡时钟输出端口&#xff0c;输出频率在1MHz~5MHz之间可设 ⚫ 位传输方向可配置&#xff0c;支持MSB First或LS…

初识C++之C++中的IO流

目录 一、C语言中的输入与输入 二、流 三、C中的流 四、C中的文件IO流 1. 二进制文件 1.1 打开文件 1.2 向文件写入数据 1.3 从文件读取数据 1.4 关闭文件 1.5 综合使用 2. 文本读写 一、C语言中的输入与输入 在C语言中&#xff0c;我们最长使用的输入输出方式就是…

0基础小白简单入门使用emqx的webhook+规则实现Mysql数据持久化

EMQX (opens new window)是一款大规模可弹性伸缩的云原生分布式物联网 MQTT (opens new window)消息服务器。 作为全球最具扩展性的 MQTT 消息服务器&#xff0c;EMQX 提供了高效可靠海量物联网设备连接&#xff0c;能够高性能实时移动与处理消息和事件流数据&#xff0c;帮助…

EndNote X9 参考文献附录列表 格式调整

文章目录 1 参考文献附录列表 格式调整2 EndNote X9 插入参考文献常见问题总结3 EndNote X9 快速上手教程&#xff08;毕业论文参考文献管理器&#xff09; 1 参考文献附录列表 格式调整 注意&#xff1a;这里讲的是对齐格式&#xff0c; 文献规范格式参考EndNote X9 快速上手…

发布会彩排哪些内容?要注意哪些细节?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 相信发布会前小伙伴都会进行彩排&#xff0c;对发布会的细节&#xff0c;流程&#xff0c;各个工种如何配合进行提前的演练&#xff0c;那么发布会彩排哪些内容&#xff0c;要注意哪些细…

数据结构-图的创建与深度优先遍历DFS(邻接矩阵-动态类型)

图的创建&#xff1a; 我们先构建一个无向图&#xff1a;如图所示 根据规定&#xff0c;如果两个顶点相连&#xff0c;则两顶点的边改为1&#xff0c;否则为0&#xff0c;我们用数组指针arcs来指向标记是否有边的数组。 1.先创建结构体&#xff0c;因为都为动态所以我们都先定…

RabbitMQ详解(四):SpringBoot整合MQ

SpringBoot整合MQ 需要创建两个springboot项目&#xff0c;一个springboot_rabbitmq_producer生产者&#xff0c;一个springboot_rabbitmq_consumer消费者 fanout模式&#xff08;配置文件方式&#xff09; 定义生产者 创建生产者工程 springboot_rabbitmq_producer pom.x…

DragonflyDB 安装使用

前言 全世界最快的内存数据库 Dragonfly是一种针对现代应用程序负荷需求而构建的内存数据库&#xff0c;完全兼容Redis和Memcached的 API&#xff0c;迁移时无需修改任何代码。相比于这些传统的内存数据库&#xff0c;Dragonfly提供了其25倍的吞吐量&#xff0c;高缓存命中率和…

演化博弈模型简介

演化博弈模型简介 文章目录 演化博弈模型简介[toc]1 演化博弈思想2 演化博弈关注的问题3 复制动态中的博弈 1 演化博弈思想 传统博弈苛刻假设&#xff1a; 完全理性完全信息 演化博弈论&#xff1a;演化博弈论(Evolutionary Game Theory)把博弈理论分析和动态演化过程分析结…

【python】Pandas库用法详解!

pandas 是基于NumPy 的一种工具&#xff0c;该工具是为解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型&#xff0c;提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现&#xff0c;它是使Py…

Android系统启动流程(三)——属性服务

1 属性服务 属性服务的启动在init进程中&#xff0c;init进程初始化的第二阶段初始化中启动了属性服务。 system/core/init/init.cpp int SecondStageMain(int argc, char** argv) {...PropertyInit();...StartPropertyService(&property_fd);...2 启动属性服务 system/…

Win系统软件闪屏/Edge闪屏/Office闪屏 - 解决方案

Win系统软件闪屏/Edge闪屏/Office闪屏 - 解决方案 前言原因解决方案方案1&#xff08;推荐&#xff09;&#xff1a;重新安装核显驱动方案2&#xff1a;软件使用独显方案3&#xff1a;软件关闭硬件加速 前言 使用Win10及以上系统时&#xff0c;可能会出现频繁闪现黑屏的状态&a…

【jupyter】mac os系统下的jupyter的实用技巧

Jupyter notebook是一个开源的web应用&#xff0c;可以让你创建和分享包含代码、公式、可视化和叙述文本的文档。它可以用于数据清洗和转换、数值模拟、统计建模、数据可视化、机器学习等多种用途。 在mac os系统下&#xff0c;有多种方法可以安装jupyter notebook&#xff0c…

十大生产力神器,包括5大jupyter插件和五个提升python研发生产力的神器

JupyterLab&#xff1a;一款下一代的笔记本界面&#xff0c;支持多种编程语言&#xff0c;包括python。它具有灵活的界面&#xff0c;可以配置和安排数据科学、科学计算、计算新闻和机器学习等领域的工作流程。 Voil&#xff1a;一款可以将笔记本转换为安全、独立的web应用程序…

将字符串数组转换为字符串类型

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 当你在Java编程中需要将一个字符数组转换为字符串类型时&#xff0c;你可以使用Java内置的String类提供的方法。在本文中&#xff0c;笔者将介绍两种将字符数组转换为…