目录

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. 缺点:由于要在运行时动态分配内存,存取速度较慢。
  • 方法区:存放类对象(字节码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相关知识——内存分布和垃圾回收机制相关推荐

  1. 详解JVM内存管理与垃圾回收机制2 - 何为垃圾

    随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...

  2. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  3. JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)

    转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...

  4. Python的内存管理与垃圾回收机制

    在使用真格量化时,一些用户希望了解如何来提高系统性能.通过了解Python的内存管理和垃圾回收机制,我们可以有针对性地去提高策略代码性能. Python内存管理机制 Python的内存管理机制:引入计 ...

  5. JavaScript内存分配及垃圾回收机制

    JavaScript内存分配及垃圾回收机制 简介 像C语言这样的高级语言一般都有底层的内存管理接口,比如 malloc()和free().另一方面,JavaScript创建变量(对象,字符串等)时分配 ...

  6. 复习Javascript专题(二):闭包,内存,以及垃圾回收机制

    1.什么是闭包?闭包有啥特性以及存在什么问题? 概念:闭包是指有权访问另一个函数作用域中的变量的函数.下面的outer就形成了一个闭包: function outer(){const name='na ...

  7. Python内存管理以及垃圾回收机制

    垃圾回收:用通俗点的语言解释就是内存管理和垃圾回收的过程. 大管家refchain 在Python的C源码中有一个名为refchain的环状双向链表,这个链表就比较厉害了,因为Python程序中一旦创 ...

  8. python内存的回收机制_python的内存管理和垃圾回收机制详解

    简单来说python的内存管理机制有三种 1)引用计数 2)垃圾回收 3)内存池 接下来我们来详细讲解这三种管理机制 1,引用计数: 引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用 ...

  9. JS内存泄漏与垃圾回收机制

    来源 | http://www.fly63.com/article/detial/10087 由于字符串.对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配.JavaScri ...

  10. JVM内存模型和垃圾回收机制

    JVM内存模型 根据Java虚拟机规范,Java数据区域分为五大数据区域. 其中方法区和堆是所有线程共享的,虚拟机栈.本地方法栈和程序计数器则为线程私有的. 有的博客称方法区是永久代,那是因为前者是J ...

最新文章

  1. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏
  2. Swift中出现“no such module cocoa”的错误
  3. python第三方库之Django学习笔记一
  4. ARTS打卡计划第六周
  5. python读取文件按行分割字符串_python中分割字符串split切割并选择输出 逐行读取文件后字符串拼接...
  6. 继承 WebMvcConfigurationSupport类后无法访问Swagger页面问题
  7. ue4 运行禁用鼠标_[UE4] VS code使用LuaPanda断点调试
  8. python 金融可视化_用 Python 进行金融数据可视化
  9. IIoT 安防保卫战一触即发,Fortinet 亮剑
  10. springMVC的流程
  11. jstack 线程状态分析_面试官:说说你是怎么用JDK监控和故障处理工具的吧?例如jstack...
  12. keil 在多字节的目标代码页中 没有此unicode_Go语言之父带你重新认识字符串、字节、rune和字符
  13. 30 岁成 AI 顶尖科学家,这位阿里副总裁厉害了
  14. paip.提升开发效率----JAVA网站
  15. MIF/MID格式简介
  16. 损失函数、代价函数、目标函数、适应度函数的区别与联系
  17. Counterfit 部署教程
  18. python查看微信撤回消息怎么弄_Python3爬虫查看微信撤回消息
  19. android pc控制工具,电脑控制iPhone 或Android方法?透过这款工具就能实现
  20. Sencha Cmd的简介

热门文章

  1. html5音乐加大音量,怎么调大音乐声音 mp3音量增大器介绍【图解】
  2. ENVI学习总结(十二)——基于改进的 CASA 模型反演 NPP
  3. java定时每小时_java 定时任务,每日运行和每小时运行。
  4. 光纤上网究竟是如何实现的?
  5. 计算T临界值分布的表
  6. Little VGL + code::blocks + 模拟器
  7. python实现鼠标键盘事件_鼠标与键盘操作事件
  8. Unity url编码转换
  9. 支持udp转发的云服务器,云服务器转发udp原理
  10. 利用51单片机输出PWM波