JVM相关知识——内存分布和垃圾回收机制
目录
1.JVM的概念
1.1JVM执行流程
2.JRE/JDK/JVM之间的关系
3.有关JVM的经典问题
3.1 JVM的内存布局
3.1.1内存布局中的异常问题
3.2类加载机制
3.2.1类加载的流程(5个)
3.3类加载机制(双亲委派机制)
3.4 垃圾回收
3.4.1垃圾回收的概念
3.4.2垃圾回收的内存有哪些
3.4.3 如何找到垃圾回收的对象
3.4.4如何回收垃圾
1.JVM的概念
JVM :Java虚拟机( Java Virtual Machine )
虚拟机是:通过软件模拟的具有完整硬件功能的、运行在一个完全隔离环境中的完整计算机系统。
常见的虚拟机:JVM、VMwave、Virtual Box。
JVM 和其他两个虚拟机的区别:
1. VMwave、VirtualBox是通过 软件 模拟物理CPU的指令集,物理系统中有很多寄存器。
2. JVM(解释器)则是通过软件模拟Java字节码的指令集,JVM主要保留了PC寄存器,其他的寄存器都进行了裁剪。
JVM 是 Java 运行的基础,也是实现一次编译到处执行的关键
1.1JVM执行流程
1.程序执行前,把java代码转换成字节码(class文件),JVM 首先把字节码通过类加载器(ClassLoader)加载到内存中 运行时数据区(Runtime Data Area) ,而字节码文件是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是这4个主要组成部 分的职责与功能
JVM 主要分为以下 4 个部分,来执行 Java 程序:
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface)
2.JRE/JDK/JVM之间的关系
- JDK(Java Development Kit):Java开发工具包,程序开发者用来编译、调试Java程序,它也是Java程序,也需要JRE才能运行。
- JRE(Java Runtime Environment):Java运行环境,所有的Java程序在JRE下才能运行。
- JVM(Java Virual Machine):Java虚拟机,支持跨平台。(JVM的作用:将字节码class文件解释为机器码文件。)
关系:JDK包含JRE JRE包含JVM
Java运行步骤:java源码—javac编译器—>字节码文件—Java解释器—>机器码文件。
3.有关JVM的经典问题
3.1 JVM的内存布局
详细可参考文章:深度理解JAVA中的栈、堆、对象、方法区、类和他们之间的关系
JVM 运行时 数据区域也叫内存布局,(分为:堆、栈、程序计数器、方法区)和 Java 内存模型(JMM)完全不同; JVM本质是一个Java进程,JVM启动后就会从操作系统处申请到一块内存
- 堆:存放new的对象(即成员变量)和数组(特殊的对象)
- 1. 存储的全部是对象,每个对象包含一个与之对应的class信息(得到操作指)
2. jvm只有一个堆区(heap)被所有线程共享,只存放对象本身。
3. 堆的优势:可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。
4. 缺点:由于要在运行时动态分配内存,存取速度较慢。
- 1. 存储的全部是对象,每个对象包含一个与之对应的class信息(得到操作指)
- 方法区:存放类对象(字节码class文件(二进制文件,就是一些指令)被JVM加载到内存,就成了类对象),类的static成员也是类属性(静态变量)(类对象包含:类各种属性的名字、类型、访问权限;类的各种方法的名字、参数类型、返回类型、访问权限、方法实现的二进制代码)
- 程序计数器:存放内存地址(下一步执行指令的地址),是内存区域中最小的部分
- 栈: 存放局部变量 (方法下的变量)
每个线程 都有自己的 栈 和 程序计数器
本地方法栈:JVM内部的方法
虚拟机栈:给上层java代码使用的
(1)如下代码所示:变量t在方法下面,属于局部变量,存在于栈里
(2)如下代码所示:变量t是 成员变量,存在于堆里
(3)如下代码所示:变量t是 静态成员变量(类对象),存在于方法区
3.1.1内存布局中的异常问题
(1)堆溢出:
堆用于存储对象实例(成员变量),只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免来GC清除这些对象,那么在对象数量达到最大堆容量后就会产生内存溢出异常。
典型场景:无限递归(堆容量不够)
- 内存泄漏 : 泄漏对象无法被GC
- 内存溢出 : 内存对象确实还应该存活。此时要根据JVM堆参数与物理内存相比较检查是否还应该把JVM堆内存调大;或者检查对象的生命周期是否过长。
(2)栈溢出:
- 如果线程请求的栈深度 > 虚拟机所允许的最大深度,抛出StackOverFlow异常
- 如果虚拟机在拓展栈时无法申请到足够的内存空间,抛出OOM异常
3.2类加载机制
Java中的 类加载 是JVM中一个很核心的流程,就是将字节码class文件站换乘JVM中的类对象。
一个类的生命周期如下图(7个):
类加载:.class文件(编译器生成)---->类对象的过程
3.2.1类加载的流程(5个)
(1)加载 Loading 阶段,Java虚拟机需完成:
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构------>方法区的运行时数据结构。
- 内存中生成代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
(2) 验证
验证是连接阶段的第一步,目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,确保其不会危害虚拟机自身的安全。
(3) 准备
是正式为类中定义的变量(静态变量,static修饰的)分配内存并设置类变量初始值
(4)解析
是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程(也即是:初始化常量)
(5)初始化
Java 虚拟机真正开始执行类中编写的 Java 程序代码,初始化阶段就是执行类构造器方法的过程。(初始化静态变量、static静态代码块的执行,在对象实例化之前完成)
Java 虚拟机的角度,只存在两种不同的类加载器:
- 启动类加载器(Bootstrap ClassLoader):用 C++ 语言实现,是虚拟机的一部分;
- 其他所有的类加载器:由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。
3.3类加载机制(双亲委派机制)
JVM是三层架构,类的加载是通过双亲委派模型(类加载器之间的层次关系)来完成 :
这个过程也即是定义三个目录的优先级:标准库>>扩展库>>自定义库
- 启动类加载器Bootstrap:负责加载java标准库中的一些类:
Java_HOME/lib
目录中的类库 - 扩展类加载器ExtClassLoader:加载JVM扩展库涉及的类:
Java_HOME/lib/ext
目录的类库 - 应用程序类加载器AppClassLoader:加载程序员自己写的类:用户路径上的类库
- 自定义的类加载器:通过继承
java.lang.ClassLoader
实现
如果一个类加载器接收到类加载的请求(从应用程序类加载器AppClassLoader开始,触发类加载),AppClassLoader把请求委派给父加载器去完成(直至走到Bootstrap,他无法再向上层继续委派,只能在自己的目录下寻找符合的类,若找到就进行加载),若Bootstrap无法挖完成加载请求时,子加载器才会尝试自己去加载。这过程中所有的加载请求最终都会传送到启动类加载器中
双亲委派模型有什么好处:
- 确保安全,避免Java核心类库被修改;
- 避免重复加载
- 保证类的唯一性.
3.4 垃圾回收
3.4.1垃圾回收的概念
注意!!! 回收内存(死亡对象的回收),释放内存(因为内存是有限的)
程序在使用内存时,才能申请内库存空间,不使用了则需要释放,确保后续进程有足够的空间
内存泄漏: 程序一直申请内存,但不释放,导致内存越来越少,直至耗尽,此时其他进程若想在申请内存,就申请不到。这种现象成为内存泄漏
C++手动回收内存:申请内存的人负责释放内存
java垃圾回收机制:无论谁申请内存,由一个固定的角色(JVM)统一来释放内存
1)垃圾回收机制的优点:
- 能够很好地保障不出现内存泄露的情况(但不是100%)
2)垃圾回收机制的缺点:
- 需要消耗额外的系统资源
- 内存是方存在延时(内存不用之后,JVM不是里面就释放,可能会间隔一个延迟时间)
- 可能导致出现STW(stop the world)问题: 当前进程不能正常工作了,只能停下来,由JVM先把内存释放完之后才能继续进行(延时的时间过长)
3.4.2垃圾回收的内存有哪些
(垃圾回收的对象:一般指 堆 )
内存包含四部分:堆、方法区、程序计数器、栈
- 程序计数器、栈和具体的线程绑定在一起,会进行自动释放(代码块/线程结束时,内存就释放)
- 堆、方法区就是垃圾回收的内容(尤其是堆); 而方法区里是“类对象”,是通过“类加载”得到的,对方法取得垃圾回收,相当于“类卸载”
对于堆上的内存,具体回收的内容是???
不使用的成员变量
整个堆的内存分布:
堆上,存放的是new·出来的成员变量对象,包括三种:
- 完全要使用的对象
- 完全不使用:(回收)
- 一般要使用,一般不使用
3.4.3 如何找到垃圾回收的对象
先找出垃圾,再回收
如何找出垃圾??标记垃圾??判定垃圾??
(1)引用计数
给对象增加一个引用计数器,每当有一个地方引用它时,计数器+1;当引用失效时,计数器就-1;任何时刻 计数器为0 的对象 不再被使用,即对象已"死"。利用引用,判断对象是否“已死”
JVM中 不选用 引用计数法 来管理内存,因为引用计数法无法解决对象的循 环引用问题
如下代码所示,两个引用均指向null,则说明该对象没有引用了(被认为是垃圾,引用计数是0,可以回收):
Test a = new Test();
Test b = a;
a = null;
b = null;
(2)可达性分析
从一组初始位置(GCRoot)出发,向下进行深度遍历,把所有能访问到的对象标记成“可达”(可以被访问到),而不可达的对象(没有标记到)就是垃圾。判断对象是否“存活”
JVM中存在 一个/一组 线程 来周期性的遍历 进行 可达性分析(找出不可达的对象进行垃圾回收)
初始位置GCRoot可从如下3种位置出发:
- 栈上的局部变量表中的引用
- 常量池里面的引用指向的对象
- 方法区中,引用类型的静态成员变量
3.4.4如何回收垃圾
经典的3种垃圾回收方法:
(1)标记-清除
上图引入了额外的问题:内存碎片(空闲内存、正在使用的内存是交替出现的,若申请大块连续内存空间可能会分配失败),内存碎片累积会导致:
- 效率问题 : 标记和清除这两个过程的效率都不高
- 空间问题 : 标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行中需要分配较大对象时,无法找到足够连续内存而不得不提前触发另一次垃圾收集。
(2)复制算法
可解决内存碎片的问题,它将 可用内存 按容量划分为 大小相等的两块(每次只使用一块),当需进行垃圾回收时,将此区域存活着的对象复制到另一块上面,然后再清理掉使用过的内存。
每次都是对整个半区进行内存回收,内存分配时不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配即可。
- 可用空间只有一半
- 若回收对象较少,需要复制的就很多,开销就很大
(3)标记-整理
解决复制算法空间利用率较低的问题,(类似顺序表删除元素,把不需要回收的对象放到需要删除的地方),开销比赋值算法更大
(4)分代回收
通过内存中的区域划分,实现不同区域和不同的垃圾回收策略;
- 根据对象存活周期的不同将内存划分:把Java堆分为:新生代和老年代。
- 新生代中(存放刚new的对象):每次垃圾回收都有大批对象死去,只有少量存活,采用复制算法;
- 老年代中对象存活率高、没有额外空间进行分配担保,采用"标记-清理"或者"标记-整理"算法。
- 一个新new的对象存放于新生代;经历过一轮GC还存活的对象放于幸存区
- 幸存区中的对象会经过多次GC,每一次没被回收的对象都通过 复制算法 拷贝到另外的生存区
- 在生存区中经历多次GC仍存在就把它拷贝到老年代
- 进入老年代,JVM认为该对象是持久存在的(GC扫描频率就降低了)
如果对象特别大也会存放于老年代(因为新生代会多次拷贝,会导致开销很大)
JVM相关知识——内存分布和垃圾回收机制相关推荐
- 详解JVM内存管理与垃圾回收机制2 - 何为垃圾
随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...
- 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型
在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...
- JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)
转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...
- Python的内存管理与垃圾回收机制
在使用真格量化时,一些用户希望了解如何来提高系统性能.通过了解Python的内存管理和垃圾回收机制,我们可以有针对性地去提高策略代码性能. Python内存管理机制 Python的内存管理机制:引入计 ...
- JavaScript内存分配及垃圾回收机制
JavaScript内存分配及垃圾回收机制 简介 像C语言这样的高级语言一般都有底层的内存管理接口,比如 malloc()和free().另一方面,JavaScript创建变量(对象,字符串等)时分配 ...
- 复习Javascript专题(二):闭包,内存,以及垃圾回收机制
1.什么是闭包?闭包有啥特性以及存在什么问题? 概念:闭包是指有权访问另一个函数作用域中的变量的函数.下面的outer就形成了一个闭包: function outer(){const name='na ...
- Python内存管理以及垃圾回收机制
垃圾回收:用通俗点的语言解释就是内存管理和垃圾回收的过程. 大管家refchain 在Python的C源码中有一个名为refchain的环状双向链表,这个链表就比较厉害了,因为Python程序中一旦创 ...
- python内存的回收机制_python的内存管理和垃圾回收机制详解
简单来说python的内存管理机制有三种 1)引用计数 2)垃圾回收 3)内存池 接下来我们来详细讲解这三种管理机制 1,引用计数: 引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用 ...
- JS内存泄漏与垃圾回收机制
来源 | http://www.fly63.com/article/detial/10087 由于字符串.对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配.JavaScri ...
- JVM内存模型和垃圾回收机制
JVM内存模型 根据Java虚拟机规范,Java数据区域分为五大数据区域. 其中方法区和堆是所有线程共享的,虚拟机栈.本地方法栈和程序计数器则为线程私有的. 有的博客称方法区是永久代,那是因为前者是J ...
最新文章
- [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏
- Swift中出现“no such module cocoa”的错误
- python第三方库之Django学习笔记一
- ARTS打卡计划第六周
- python读取文件按行分割字符串_python中分割字符串split切割并选择输出 逐行读取文件后字符串拼接...
- 继承 WebMvcConfigurationSupport类后无法访问Swagger页面问题
- ue4 运行禁用鼠标_[UE4] VS code使用LuaPanda断点调试
- python 金融可视化_用 Python 进行金融数据可视化
- IIoT 安防保卫战一触即发,Fortinet 亮剑
- springMVC的流程
- jstack 线程状态分析_面试官:说说你是怎么用JDK监控和故障处理工具的吧?例如jstack...
- keil 在多字节的目标代码页中 没有此unicode_Go语言之父带你重新认识字符串、字节、rune和字符
- 30 岁成 AI 顶尖科学家,这位阿里副总裁厉害了
- paip.提升开发效率----JAVA网站
- MIF/MID格式简介
- 损失函数、代价函数、目标函数、适应度函数的区别与联系
- Counterfit 部署教程
- python查看微信撤回消息怎么弄_Python3爬虫查看微信撤回消息
- android pc控制工具,电脑控制iPhone 或Android方法?透过这款工具就能实现
- Sencha Cmd的简介