| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> Java-jvm -> 正文阅读 |
|
[Java知识库]Java-jvm |
这里写自定义目录标题jvm内存模型根据jvm规范 jvm内存分为虚拟机栈,堆,方法区,程序计数器,本地方法栈五个部分. 与程序计数器一样,java虚拟机栈(java virtual machine stacks) 也是线程私有的,它的生命周期和线程相同,虚拟机栈描述的是java方法执行的内存模型;每个方法被执行的时候都会创建一个栈帧(stack frame)用于存储局部变量表,操作栈,动态链接,方法出口等信息,每个方法被调用直至执行完成的过程,就对应栈帧在虚拟机栈中从入栈到出栈的过程. 本地方法栈 本地方法栈(N阿提测Method stacks) 与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机所执行java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用的Native方法服务,虚拟机规范中对本地方法栈中的方法使用的语言,使用方式与数据结构并没有强制规定.因此具体的虚拟机可以自由实现它.甚至有的虚拟机(譬如sun hotSpot虚拟机)直接将本地方法栈和虚拟机栈合二为一.与虚拟机栈一样,本地方法栈区域也会抛出stackOverFlowError和OutOfMemoryError异常 对于大多数应用来说,java堆(java heap)是java虚拟机所管理的内存最大的一块,java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象对象实例都在这里分配内存,这一点在java虚拟机规范中的描述是:所有的对象和数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配,标量替换,优化技术将会导致一些微妙的变化发生,所有的对象都在堆上也渐渐变得不是那么绝对了. 类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分: Permanent Space 永久存储区 Young Generation Space 新生区 新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1区也满了呢?再移动到养老区。 Tenure generation space养老区 方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存 储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽 然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non- Heap(非堆),目的应该是与Java 堆区分开来。 对于习惯在HotSpot 虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区 称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot 虚 拟机的设计团队选择把GC 分代收集扩展至方法区,或者说使用永久代来实现方法区而 已。对于其他虚拟机(如BEA JRockit、IBM J9 等)来说是不存在永久代的概念的。即 使是HotSpot 虚拟机本身,根据官方发布的路线图信息,现在也有放弃永久代并“搬家” 至Native Memory 来实现方法区的规划了。 Java 虚拟机规范对这个区域的限制非常宽松,除了和Java 堆一样不需要连续的内 存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾 收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一 样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸 载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件 相当苛刻,但是这部分区域的回收确实是有必要的。在Sun 公司的BUG 列表中,曾出 现过的若干个严重的BUG 就是由于低版本的HotSpot 虚拟机对此区域未完全回收而导 致内存泄漏。 根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。 运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有 类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放 到方法区的运行时常量池中。 Java 虚拟机对Class 文件的每一部分(自然也包括常量池)的格式都有严格的规 定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、 装载和执行。但对于运行时常量池,Java 虚拟机规范没有做任何细节的要求,不同的 提供商实现的虚拟机可以按照自己的需要来实现这个内存区域。不过,一般来说,除 了保存Class 文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常 量池中①。 运行时常量池相对于Class 文件常量池的另外一个重要特征是具备动态性,Java 语 言并不要求常量一定只能在编译期产生,也就是并非预置入Class 文件中常量池的内容 才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发 人员利用得比较多的便是String 类的intern() 方法。 既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无 法再申请到内存时会抛出OutOfMemoryError 异常 6、直接内存 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现,所以我们放到这里一起讲解。 在JDK 1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel) 与缓冲区(Buffer)的I/O 方式,它可以使用Native 函数库直接分配堆外内存,然 后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块内存的引用进行 操作。这样能在一些场景中显著提高性能,因为避免了在Java 堆和Native 堆中来 回复制数据。 显然,本机直接内存的分配不会受到Java 堆大小的限制,但是,既然是内存,则 肯定还是会受到本机总内存(包括RAM 及SWAP 区或者分页文件)的大小及处理器 寻址空间的限制。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx 等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制 (包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError 异常。 逻辑内存模型我们已经看到了,那当我们建立一个对象的时候是怎么进行访问的呢? 在Java 语言中,对象访问是如何进行的?对象访问在Java 语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及Java 栈、Java 堆、方法区这三个最重要内存区 域之间的关联关系,如下面的这句代码: Object obj = new Object(); 假设这句代码出现在方法体中,那“Object obj”这部分的语义将会反映到Java 栈 的本地变量表中,作为一个reference 类型数据出现。而“new Object()”这部分的语义 将会反映到Java 堆中,形成一块存储了Object 类型所有实例数据值(Instance Data,对 象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布 局(Object Memory Layout)的不同,这块内存的长度是不固定的。另外,在Java 堆中 还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地 址信息,这些类型数据则存储在方法区中。 由于reference 类型在Java 虚拟机规范里面只规定了一个指向对象的引用,并没有 定义这个引用应该通过哪种方式去定位,以及访问到Java 堆中的对象的具体位置,因此 不同虚拟机实现的对象访问方式会有所不同,主流的访问方式有两种:使用句柄和直接 指针。 如果使用句柄访问方式,Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的 具体地址信息,如下图所示。 Java 堆对象的布局中就必须考虑如何放置访问类型 reference 中存 会改变句柄中的实例数据指针,而reference 本身不需要被修改。 使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开 销,由于对象的访问在Java 中非常频繁,因此这类开销积少成多后也是一项非常可观的 执行成本。就本书讨论的主要虚拟机Sun HotSpot 而言,它是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。 下面我们来看几个示例 1、Java 堆溢出 下面的程中我们限制Java 堆的大小为20MB,不可扩展(将堆的最小值-Xms 参 数与最大值-Xmx 参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDump OnOutOfMemoryError 可以让虚拟机在出现内存溢出异常时Dump 出当前的内存堆转储 快照以便事后进行分析。 参数设置如下 space”。 要解决这个区域的异常,一般的手段是首先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump 出来的堆转储快照进行分析,重点是确认内存中的对象是 否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢 出(Memory Overflow)。图2-5 显示了使用Eclipse Memory Analyzer 打开的堆转储快 照文件。 如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots 的引用链。于是就 能找到泄漏对象是通过怎样的路径与GC Roots 相关联并导致垃圾收集器无法自动回收 它们的。掌握了泄漏对象的类型信息,以及GC Roots 引用链的信息,就可以比较准确 地定位出泄漏代码的位置。 如果不存在泄漏,换句话说就是内存中的对象确实都还必须存活着,那就应当检查 虚拟机的堆参数(-Xmx 与-Xms),与机器物理内存对比看是否还可以调大,从代码上 检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期 的内存消耗。 以上是处理Java 堆内存问题的简略思路,处理这些问题所需要的知识、工具与经验 在后面的几次分享中我会做一些额外的分析。 package com.yhj.jvm.memory.stack; /**
*/ public class StackOverFlow {
} 3、常量池溢出(常量池都有哪些信息,我们在后续的JVM类文件结构中详细描述) package com.yhj.jvm.memory.constant; import java.util.ArrayList; import java.util.List; /**
*/ public class ConstantOutOfMemory {
} 4、方法去溢出 package com.yhj.jvm.memory.methodArea; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /**
*/ public class MethodAreaOutOfMemory {
} /**
*/ class TestCase{ } 5、直接内存溢出 package com.yhj.jvm.memory.directoryMemory; import java.lang.reflect.Field; import sun.misc.Unsafe; /**
*/ public class DirectoryMemoryOutOfmemory {
欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
功能快捷键撤销:Ctrl/Command + Z 合理的创建标题,有助于目录的生成直接输入1次#,并按下space后,将生成1级标题。 如何改变文本的样式强调文本 强调文本 加粗文本 加粗文本 标记文本
H2O is是液体。 210 运算结果是 1024. 插入链接与图片链接: link. 图片: 带尺寸的图片: 居中的图片: 居中并且带尺寸的图片: 当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。 如何插入一段漂亮的代码片去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的
生成一个适合你的列表
创建一个表格一个简单的表格是这么创建的:
设定内容居中、居左、居右使用
SmartyPantsSmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
创建一个自定义列表
如何创建一个注脚一个具有注脚的文本。2 注释也是必不可少的Markdown将文本转换为 HTML。 KaTeX数学公式您可以使用渲染LaTeX数学表达式 KaTeX: Gamma公式展示 Γ ( n ) = ( n ? 1 ) ! ? n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n?1)!?n∈N 是通过欧拉积分 Γ ( z ) = ∫ 0 ∞ t z ? 1 e ? t d t ? . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞?tz?1e?tdt.
新的甘特图功能,丰富你的文章
UML 图表可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图: 这将产生一个流程图。:
链接
长方形
圆
圆角长方形
菱形
FLowchart流程图我们依旧会支持flowchart的流程图:
导出与导入导出如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。 导入如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年4日历 | -2025/4/12 2:58:11- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |