jvm--3.内存管理


5.JVM内存管理
JAVA虚拟机在执行java程序的过程中,会把它管理的内存分成若干个不同的数据区域。
------------------------------------------------------------------------------------—
| 运行时数据区 |
| ----------- -------- ----------------- |
| | 方法区 | | 栈 | | 本地方法栈 | |
| | | | | | | |
| ----------- -------- ----------------- |
| |
| --------------------------- ----------------- |
| | 堆 | | 程序计数器 | |
| | | | | |
| --------------------------- ----------------- |
| ⬇️ ⬆️ ⬇️ ⬆️ |
| ---------------------------- ------------------ ----------------- |
| | 执行引擎 | | 本地库接口 | ➡️ | 本地方法库 | |
| | | | | | | |
| ---------------------------- ------------------ ----------------— |
|
| 其中,堆和方法区,是所有线程共有区;
| 栈,本地方法栈,程序计数器,是线程私有区。
|------------------------------------------------------------------------------------
(1) 内存区域
a.程序计数器(Program Counter Register),线程私有,不会抛出任何内存异常
I.可以这么理解,当前线程所执行字节码的行号指示器。字节码解释器,就是通过程序计数器的值,来选取下一条要执行的字节码指令(分支、循环、跳转等基础功能都需要依赖计数器)。
II.java虚拟机的多线程是通过,各个线程之间轮流切换并分配内存来实现的。在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换回来之后能够
恢复到正确的执行位置。每条线程都需要一个独立的程序计数器。
III.如果线程正在执行的是一个java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的是native方法,计数器的值为空。

b.java虚拟机栈 (Java Virtual Machine Stack) , 线程私有,会有 StackOverFlow 和 OutOfMemoryError异常 ,通过-Xss分配内存大小
I.每个方法在执行时,都会创建一个栈帧(Stack Frame),用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
每一个方法从调用到执行完成的过程,就对应一个 栈帧 在虚拟机栈中,入栈到出栈的过程。

II.栈中的局部变量表,所需的内存空间,在编译器间完成分配。在进入一个方法时,这个方法需要在帧(Stack Frame) 中分配多大的内存空间是确定的,在这个方法运行期间,
不会 改变 局部变量表 所占用 内存空间 的大小。

III.当java启动一个线程时,虚拟机会计算出这个线程所需要的栈深度,(比如10),当线程请求的栈深度(每调一个方法压一个栈帧,用掉一个栈深度),大于虚拟机给Stack分配的
栈深度,会抛出StackOverFlow异常。(用javap javap -verbose Test 查看程序的字节码,Code 属性, stack=2 , 可以查看运行的详细过程,包括栈深度,和每个栈帧需要多少个slot)
当一个可扩展栈(栈有可扩展的有固定长度的,由使用的JAVA虚拟决定的),动态扩展时,无法请求到足够的内存(比如我需要10M内存,但是JVM只给我5M),会抛出,
OutOfMemoryError异常。

c.本地方法栈 (Native Method Stack) ,线程私有,会有 StackOverFlow 和 OutOfMemoryError异常,通过-Xss分配内存大小
I.和java虚拟机栈基本一样。区别不过是,
JVM Stack 为 虚拟机 执行java方法 服务;
Native Method Stack 为 虚拟机 执行本地方法服务
II.也会抛出StackOverFlow 和 OutOfMemoryError异常

d.java堆 , 线程共享 , 会抛出OutOfMemoryError异常。,通过-Xms分配内存最小值,-Xmx分配内存最大值
I.存放 对象实例 和 数组。
II.是垃圾回收的主要区域。
III. java堆,可以处于物理上的不连续空间,逻辑上连续即可。可动态扩展,通过-Xms控制大小。
IV.如果堆中没有完成内存分配,并且堆也无法扩展是,将会抛出OutOfMemoryError异常。

e.方法区 , 线程共享 , 会抛出OutOfMemoryError异常。
I.用于存储,已经被虚拟机加载的,类的信息、常量、静态变量、编译后的代码等数据
II.不需要连续的内存,可以选择固定大小和可扩展
III.当方法区无法完成内存分配需求时,会抛出OutOfMemoryError异常。
e-slave. 运行时常量池,会抛出OutOfMemoryError异常。
是方法区的一部分。
I.Class文件中,除了有类的 版本、 字段、方法、接口、等描述信息外,还有一项就是常量池(Constant Pool Table),用于存放编译期生成的,
各种字面变量和符号引用(),这部分将在类加载后进入方法区的常量池。
II.并非预置在Class文件中常量池中的内容,才能进入方法区;运行期间也可能将新的常量放入池中。

f.直接内存,并不是java虚拟机内存的一部分,而是机器内存的一部分,会抛出OutOfMemoryError异常
I.NIO引入了一种类似于 通道(Channel) 和 缓冲区(Buffer) ,可以使用Native函数库直接分配堆外内存。
然后,通过一个存储在java堆中的,DirectByteBuffer对象作为这块内存的引用进行操作。
这样避免了在java堆和native堆中来回复制数据,提高了性能。
II.当直接内存和JVM内存之和大于机器内存时,抛出OutOfMemoryError内存。

(2)对象创建细节
a.内存分配方式
I.指针碰撞, 如果java堆中内存是绝对规整的,为对象分配空间的任务,等同于把一块确定大小的内存从java堆中划分出来。
如果java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为临界点,那么分配内存就是,
把指针向空闲空间那边挪动等同于对象大小的距离,这种分配方式成为 指针碰撞。

II.空闲列表, 如果内存是不规则的,虚拟机就必须维护一个表,记录那些内存块是可用的,分配的时候找到一块足够大的内存块分给对象实例,
并更新列表的记录,这种方式称为 空闲列表(Free List)

b.选择哪种分配方式是java堆是否规整决定的,java堆是否规整,是由采用的垃圾收集器是否带有压缩功能决定的。因此,
使用Serial、ParNew灯光带有压缩(Compact)过程的收集器时,系统采用的分配算法是指针碰撞。
使用CMS这种基于 Mark-Sweep(标记-移除) 算法的收集器,系统采用的分配算法是空闲列表。

c.空闲列表问题,及解决方案
问题:
首先,堆是线程共有的,所以,当多线程创建对象是,有这样一个问题,当Thread A分配一块内存完成后,还没更新列表,这时Thread B给
自己的对象分配了同一块内存,这就造成了冲突。
方案:
I.对分配内存的动作,进行同步,这种造成性能下降。
II.每个线程在堆中,预先分配一小块内存作为缓冲区,称为(Thread Local Allocation Buffer , TLAB) ,哪个线程需要给自己的对象分配
内存,就在自己的TLAB上分配,只有自己的TLAB上分配完了,才需要同步锁定。通过-XX:+/-UseTLAB参数来设定。(性能调优)

d.内存分配完成后,虚拟机将分配到的内存空间初始化为零值。这一步操作保证了Java代码中可以不赋初始值就可以使用

e.接下来,虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、对象的hash-code、对象的GC分代年龄,等信息。存在对象头中。
这样从JVM的角度来说,对象创建完成。

f.对象的内存布局,对象在内存中的存储可以分为3块区域:
I.对象头 (Header) : 包括两部分信息,
第一部分,存储对象自身运行时数据,如HashCode,GC分代年龄,锁定状态标志,线程持有的锁。
第二部分,类型指针,即对象指向它的 类 元数据的指针,虚拟机通常用这个指针确定对象属于哪个类。
还有记录数组长度的信息。
II.实例数据 (Instance Data) : 程序定义的个字段的内容。包括父类和子类。
III.对齐填充 (Padding) : 占位符,换句话说,就是保证对象大小必须是 8字节(byte) 的整数倍

g.对象的访问定位
java程序需要使用栈上面的reference,引用数据来操作堆上的具体对象。
I. 句柄
这种方式,Java堆中会分配一块内存,作为句柄池,reference存储的就是对象句柄池地址。
句柄中包含了对象实例数据 (在堆上),和类型数据(类数据,在常量池)具体地址信息。
好处:GC后reference不需要修改
II.直接指针
reference存储的就是对象地址。
好处:速度快,节省了一次指针定位开销。
Sun HotSpot使用直接指针
(3)堆溢出,OutOfMemoryError 后面跟 Heap
-Xms 和 -Xmx 设置堆的最大和最小内存
堆的最小参数 -Xms 和 最大参数 -Xmx 设置为一样,就可以避免堆扩展。
a.解决思路:
I.用内存映像分析工具(如,Eclipse Memory Analyzer) 堆Dump出来的堆转储快照进行分析。
II.分析的重点是确认内存中的对象是否是必要的,即先确认是否有 内存泄漏(Memory Leak,当创建
的对象没有使用,又无法被GC回收,就是内存泄漏)
III.如果是内存泄漏,查看泄漏对象到GC Roots的引用链信息,就能找到泄漏对象是通过怎样的路径与GC Roots相关联,
并导致GC无法自动回收他们的。通过引用链信息,定位到泄漏代码的位置,review代码。
IV.如果没有内存泄漏,即,内存中的对象都必须存活。那就看虚拟机堆参数(-Xms和-Mmx)和内存相比,看是否还可以调大。
从代码上检查是否有,某些对象生命周期过长,持有时间过长的情况,尝试优化这些代码,从而减少运行期的内存消耗。
(4)栈溢出 StackOverFlow
-Xss设置栈占用内存大小。默认1024K,也就是1M
a.虚拟机启动时,有栈大小的默认参数,当所有的栈帧(Stack Frame),内存加起来超过栈内存大小时,就会抛出StackOverFlow异常。
在栈深度,默认情况下,大多数栈深度达到1000-2000帧没有问题,对于普通递归是够用了(但是栈帧大小是不确定的,所以,只能是大多数情况下。)
b.建立线程数量过多,导致内存溢出
I.操作系统,分配给每个进程的内存是有限制的。如果给一个java分配了1G内存,
虚拟机提供了参数,来控制堆和方法区所占用内存大小,如果没有指定栈占用的内存大小,忽略其它,剩余的内存 1G - 堆内存 - 方法区内存,被本地方法栈和虚拟机栈
瓜分,栈是线程私有的,栈分配的内存越大,可以建立的线程数就越少,建立新线程时候,容易把剩下的内存耗尽。这种情况,可以减少最大堆,和减少栈容量,换取更多
的线程,避免内存溢出。
(5)方法区,内存溢出 。OutOfMemoryError后面跟随PermGen
-XX:PerSize 和 -XX:MaxPermSize限制方法区大小
String.intern()是一个native方法,作用:如果字符串常量池中,已经包含一个等于此String 对象的字符串,则返回常量池中,代表此字符串的对象。
否则,将此String对象添加到常量池中。
a.Spring Hibernate在对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区,容易导致方法区的内存溢出。
b.JSP第一次运行时,要编译成java类,大量的jsp也有可能导致方法区内存溢出。

(6) 本机直接内存溢出 OutOfMemoryError Unsafe.allocateMemory
DirectMemory 容量可以通过:-XX:MaxDirectMemorySize指定,如果不指定,则默认与java堆最大值 (-Xmx)一样。
如果内存溢出,在堆的Dump文件很小,或者没有明显的异常,又或者程序中使用了NIO,可以考虑是 本机直接内存溢出。

转载于:https://www.cnblogs.com/fubaizhaizhuren/p/5938480.html

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

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

相关文章

[BZOJ2458][BeiJing2011]最小三角形

题目描述 Description Xaviera现在遇到了一个有趣的问题。平面上有N个点,Xaviera想找出周长最小的三角形。由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。为了减小问题的难度,这里的三角形也包括共线的三点。 输…

Makefile中的变量

Makefile中的变量 2007-11-03 12:03Makefile中变量有以下几个特征: 1. Makefile中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取makefile文件时进行的,这里的变量包括了使用“”定义和使用指示符“d…

小技巧集锦

2019独角兽企业重金招聘Python工程师标准>>> jackson JsonDeserialize 使用方法&#xff1a; 实现方法注解写在set方法上。 public class CustomJsonDateDeserializer extends JsonDeserializer<Date> {private SimpleDateFormat datetimeFormat new SimpleD…

interface-C#接口-统一的标准

文章目录接口的定义接口的实现实例1实例2接口的继承博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 接口是面向对象编程的一个重要技术&#xff0c;在C#中负责实现多重继承。一个接口定义一个协定&#xff0c;实现接口类或结构体必须遵守其协定…

JMeter入门(1):JMeter总体介绍及组件介绍

一、JMeter概述 JMeter就是一个测试工具&#xff0c;相比于LoadRunner等测试工具&#xff0c;此工具免费&#xff0c;且比较好用&#xff0c;但是前提当然是安装Java环境&#xff1b;JMeter可以做(1)压力测试及性能测试&#xff1b;(2)数据库测试&#xff1b;(3)Java程序的测试…

二层交换机、三层交换机和路由器的基本工作原理和三者之间的主要区别

二层交换机:二层交换技术是发展比较成熟&#xff0c;二层交换机属数据链路层设备&#xff0c;可以识别数据包中的MAC地址信息&#xff0c;根据MAC地址进行转发&#xff0c;并将这些MAC地址与对应的端口记录在自己内部的一个地址表中。 具体如下&#xff1a; &#xff08;1&…

Unity3D:视物有点眩晕的原因

设置Main Camera 的 Field of View 为100&#xff0c;看物体总觉得很不舒服。 设置为 60 就正常了。 根本原因&#xff0c;有待于分析 转载于:https://www.cnblogs.com/makebetter/p/7063694.html

使用jQuery清空file文件域的解决方案

使用jQuery清空file文件域的解决方案 var file $("#file") file.after(file.clone().val("")); file.remove();

更改mysql最大连接数

方法一&#xff1a; 打开cmd&#xff0c;用"mysql -u root -p;"命令进入mysql, 输入命令&#xff1a;show variables like "max_connections" 显示最大连接数 更改最大连接数 : set global max_connections 5000 方法二&#xff1a; 在my.ini加上 max_co…

根据HTML5 获取当前位置的经纬度【百度地图】【高德地图】

是想让地图的定位用户位置更准确一些。 查看了介绍&#xff1a; http://www.w3school.com.cn/html5/html_5_geolocation.asp 看介绍中拿数据挺简单。 <!DOCTYPE html> <html> <body> <p id"demo">点击这个按钮&#xff0c;获得您的坐标&…

C#抽象类与密封类-abstract-sealed

文章目录抽象类和抽象方法实现抽象方法接口、类和抽象类密封类博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 如果说继承是面向对象设计理论的基石&#xff0c;那么抽象理论和方法就是继承理论的顶梁柱。 抽象类和抽象方法 简单的说&#x…

vs2010快捷键

Ctrl M O: 折叠所有方法 Ctrl M M: 折叠或者展开当前方法 Ctrl M L: 展开所有方法 1、强迫智能感知&#xff1a;CtrlJ&#xff1b;2、强迫智能感知显示参数信息&#xff1a;Ctrl-Shift-空格&#xff1b;3、格式化整个块&#xff1a;CtrlKF4、检查括号匹配(在左右括号间切…

startup毕业论文

今天起得相对比较晚&#xff0c;为的是一个没有目的面试&#xff0c;去了的结果。只是打击一下自己的自信心&#xff0c;走的时候&#xff0c;面试官冷冷的说了一句&#xff0c;你的面试到此结束&#xff0c;是的&#xff0c;我并没有很伤心&#xff0c;在门外等面试的时候&…

Javascript实现信息滚动效果的方法

<html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title>向上无缝滚动</title><style>body { font-size: 12px; line-height: 24px; text-algin: center; /* 页面内容居中 */}* { ma…

C# delegate与event,委托与事件

文章目录委托示例事件实例博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 委托和事件是C#中两个比较复杂的概念&#xff0c;这篇文章介绍两个概念与基本用法&#xff0c;让大家理解C#中的事件处理机制。 委托 委托也叫代理&#xff0c;就是把…

路由器与交换机的工作原理

路由器与交换机的工作原理 计算机网络往往由许多种不同类型的网络互连连接而成。如果几个计算机网络只是在物理上连接在一起&#xff0c;它们之间并不能进行通信&#xff0c;那么这种“互连”并没有什么实际意义。因此通常在谈到“互连”时&#xff0c;就已经暗示这些相互连接的…

Java的四种引用,强弱软虚,用到的场景(转+补充)

Q1&#xff1a;引用队列是什么&#xff1f;如何使用&#xff1f;使用的场景有哪些&#xff1f; A1:oracle的api文档的描述&#xff1a; https://docs.oracle.com/javase/7/docs/api/java/lang/ref/ReferenceQueue.htmlReference queues, to which registered reference objects…

C# lambda表达式与匿名方法

文章目录匿名方法Lambda表达式实例实例博主写作不容易&#xff0c;孩子需要您鼓励 万水千山总是情 , 先点个赞行不行 C#中的匿名方法是在C#2.0引入的&#xff0c;它终结了声明委托的唯一方法是使用命名方法的时代。在C#更高版本中&#xff0c;Lambda表达式取代了匿名方法&a…

LINUx打包命令汇总

.tar 解包&#xff1a;tar xvf FileName.tar 打包&#xff1a;tar cvf FileName.tar DirName &#xff08;注&#xff1a;tar是打包&#xff0c;不是压缩&#xff01;&#xff09; ——————————————— .gz 解压1&#xff1a;gunzip FileName.gz 解压2&#xff1a;…

常用的相似度计算

在数据分析和数据挖掘的过程中&#xff0c;我们经常需要知道个体间差异的大小&#xff0c;进而评价个体的相似性和类别。最常见的是数据分析中的相关分析&#xff0c;数据挖掘中的分 类和聚类算法&#xff0c;如K最近邻&#xff08;KNN&#xff09;和K均值&#xff08;K-Means&…