当前位置: 首页 > news >正文

JVM学习笔记(持续更新)

JDK、JRE、JVM区别?

在这里插入图片描述

类加载过程

装载 验证 准备 解析 初始化
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

类加载器分类

在这里插入图片描述

双亲委派模型

在这里插入图片描述
如何打破双亲委派模型?
自定义类加载器,集成ClassLoader类重写loadClass,如Tomcat

JVM内存模型

JVM 需要使用计算机的内存,Java 程序运行中所处理的对象或者算法都会使用 JVM 的内
存空间,JVM 将内存区划分为 5 块,这样的结构称之为 JVM 内存模型。

JVM为什么进行内存区域的划分?

随着对象数量的增加,JVM 内存使用率也在增加,如果 JVM 内存使用率达到 100%,
则无法继续运行程序。为了让 JVM 内存可以被重复使用,我们需要进行垃圾回收。为了提高垃圾回收的效率JVM 将内存区域进行了划分

JVM内存划分

JVM 按照线程是否共享将内存首先分成两大类
线程独享区
当有当前线程能访问数据的区域,线程之间不能共享,随着线程的创建而创建,随着线程的销毁而被回收。线程存在的时间较短,一般不涉及垃圾回收
线程共享区
所有的线程都可以访问的区域,当线程被销毁的时候,共享区的数据不会被立即回收,需要等待达到垃圾回收的阈值才会进行回收。垃圾回收的优化,主要是优化的线程共享区。
在这里插入图片描述

程序计数器

程序计数器会记录当前线程执行指令的内存地址,只占用一小部分区域,只记录一个地址,故我们认为程序计数器不会出现内存溢出的问题。

本地方法栈

Java中有些代码的实现是依赖于其他非Java语言的(C++),本地方法栈存储的是非Java语句执行中产生的数据。一般我们认为本地方法栈不会出现内存溢出的问题。

虚拟机栈

存放当前线程中所声明的变量,包括基本数据类型的数据和引用数据类型的引用。
基本数据类型和引用数据类型划分的标准:

  • 基本数据类型:

  • 四类八种(byte【1字节】、short 【2】、int【4】、 long【8】 、 char【2~3根据编码】、 boolean【1】 、float【4】 、double【8】)
    变量在声明的时候就能确认占用内存的大小。
    引用数据类型将值的引用存放在虚拟机栈中,对象存放在堆内存中,引用数据类型占4个字节

  • 引用数据类型:
    (对象、数组、接口)
    变量在声明的时候,不能取人占用内存的大小。

栈帧

每个线程都会对应一个虚拟机栈,线程的每个方法都会创建一个栈帧,存放本次方法执行过程中所需要的数据。
如果一个线程中有多个方法嵌套调用,虚拟机栈会对栈帧进行压栈和出栈操作,正在执行的方法一定在栈顶,我们只能获取栈顶的栈帧,栈帧在虚拟机栈中先进后出。

栈帧的数据结构

在这里插入图片描述

  • 1、局部变量
    存放当前方法的局部变量,基本数据类型存值,引用数据类型存内存地址。
  • 2、操作数栈
    对方法中的变量提供计算的区域
  • 3、常量数据的引用
    常量数据会存放到方法区的常量池中,不管是基本数据类型还是引用数据类型。
  • 4、方法返回值的地址
    方法返回数据会存到计算机内存的寄存器中。
虚拟机栈溢出异常

在这里插入图片描述

修改虚拟机栈的内存大小设置栈帧的最大深度:-Xss 虚拟机栈内存大小。
一般栈帧深度达到3000~5000即可。
太小虚拟机栈容易溢出,太大导致每个线程内存占用过大,影响线程数量。

方法区

在 java8 之后,我们把方法区称之为元空间(MetaSpace),方法区在逻辑上属于堆的一部分,但一些具体机制和堆有所区别,如:一些 JVM 的方法区是可以不进行垃圾回收的,关闭 JVM 时才会释放方法区内存。所以方法区还有一个别名叫非堆,目的是和堆分开。
方法区会存储类信息、静态变量、常量(JDK8 之后不存放字符串常量)、本地机器指令
如果加载大量 class 文件,也会造成方法区内存溢出,如一个 tomcat 运行 20~30 个项目。

堆内存模型

JAVA 对象内存布局

在这里插入图片描述

对象头

MarkWord:一系列标记位(哈希码、分代年龄、锁状态标记等),在 64 位系统中占8 字节;
ClassPoint:对象对应的类信息的内存地址,在 64 位系统中占 8 字节。
Length:数组对象特有,表示数组长度,占 4 字节。

实例数据:

包含了对象的所有成员变量,大小由变量类型决定。
byte、boolean:1 字节
short:2 字节
char:2~3 字节
int、float:4 字节
long、double、引用数据类型:8 字节

对其填充

将对象大小填充为 8 字节的整数倍

JVM 内存溢出和垃圾回收机制

为什么要进行堆内存分区:
1、提高垃圾搜索效率;
2、垃圾回收后可以更好的利用内存空间,存放大对象;
3、尽可能减少GC次数

JVM 堆内存的划分

在这里插入图片描述
老年代:
对象会优先分配到新生代内存中,每次 GC 后没有回收的对象年龄加 1,年龄到15 还没有被回收,对象会存放到老年代内存中;如果对象较大,超过新生代内存的一半,对象也会存放到老年代区域。
新生代:
为了减少young区垃圾回收后的空间碎片,新生代又分为Eden区和两个Survivor区,且始终有一个 Suvivor 区保持闲置。对象会先存放到 Eden 区当中,Eden 区空间满了之后会进行 young 区的垃圾回收,之后将 young 区所有存活的对象复制到闲置的 Suvivor 区中,并清空 Eden 区和正在使用的 Survivor 区。

YoungGC 和 OldGC

YoungGC

新生代区域的垃圾回收称之为 YoungGC,也叫 MinorGC,Eden 区满后会触发YoungGC

OldGC

老年代区域的垃圾回收称之为 OldGC,也叫 MajorGC,OldGC 非常浪费性能,
所以我们的 JVM 调优要尽可能减少 OldGC 的次数, OldGC 往往伴随着 YoungGC。YoungGC+OldGC = FullGC

问题:

Survivor 区空间并不大,如果满了怎么办?

1、一般情况下 GC 会回收 95%的对象,且超过 15 次 GC 的对象会存放到 old区,所以 Survivor 区不容易满。
2、如果 Survivor 区满了,会触发担保机制,提前将对象存入 Old 区。

为什么需要 Survivor 区?

为了减少垃圾回收带来的空间碎片,空间碎片过多会频繁触发YoungGC。

为什么需要两块 Survivor 区?

为了减少 Survivor 区的空间碎片。

垃圾回收机制

如何判断一个对象是垃圾

在这里插入图片描述

引用计数法:

如果要操作对象,必须通过引用来进行。如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到。那么这个对象就成为可被回收的对象了。这种方式实现简单,效率较高,但是它无法解决循环引用的问题,因此在 Java 中并没有采用这种方式(Python 采用的是引用计数法)。

达性分析:

以一个GC Root对象作为起点进行搜索,如果在GC Roots和对象之间没有可达路径,以一个GC根对象作为起点进行搜索,如果在GC根和对象之间没有可达路径则称该对象是不可达的。

GC ROOT对象∶

  • 栈帧中的本地变量表中引用的对象。
    我们正在执行的方法是在顶部栈帧中,正在执行的方法所引用的对象必定是很重要的对象
  • 方法区中静态属性引用的对象。
    类中的变量,整个程序都要用的东西,很重要
  • 方法区中常量引用的对象。
    也是要一直使用的对象
  • 本地方法栈中引用的对象.
    存放的是一些非Java语言的对象

常见面试题补充

内存泄漏和内存溢出是一样的概念吗?

不一样,内存泄漏指不再使用的对象无法得到及时的回收,持续占用内存空间,造成内存空间的浪费。内存溢出指程序运行要用到的内存大于能提供的最大内存。内存无法内存泄漏很容易导致内存溢出,内存溢出不一定是内存泄漏导致的。

相关文章:

  • React(四)memo、useCallback、useMemo Hook
  • 机器学习各个算法的优缺点!(上篇) 建议收藏。
  • VUE阻止浏览器记住密码若依CLOUD(INPUT框密码替换圆点)
  • 一个可以自动生成随机区组试验的excel VBA小程序
  • uniapp使用数据持久化存储
  • 【JavaEE进阶】——Mybatis操作数据库(使用注解和XML方式)
  • docker部署Minio对象存储及使用
  • mongodb 编码格式 Detected BSON
  • Golang中的 defer 关键字和Python中的上下文管理with关键字
  • 数据治理-数据标准演示
  • 5岁幼儿编程:开启未来的神秘之门
  • 数据库(15)——DQL分页查询
  • npm install 出错,按照版本不匹配解决
  • springboot针对返回的response拦截处理越权问题
  • 本地电脑通过远程服务器进行ssh远程转发
  • @jsonView过滤属性
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 2019年如何成为全栈工程师?
  • dva中组件的懒加载
  • ECMAScript入门(七)--Module语法
  • HashMap剖析之内部结构
  • Netty源码解析1-Buffer
  • Swoft 源码剖析 - 代码自动更新机制
  • XML已死 ?
  • 翻译--Thinking in React
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 思考 CSS 架构
  • 译自由幺半群
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 怎样选择前端框架
  • 正则表达式小结
  • 《码出高效》学习笔记与书中错误记录
  • ionic入门之数据绑定显示-1
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​如何使用QGIS制作三维建筑
  • # C++之functional库用法整理
  • (13)DroneCAN 适配器节点(一)
  • (160)时序收敛--->(10)时序收敛十
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (ibm)Java 语言的 XPath API
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (附源码)php投票系统 毕业设计 121500
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (三)Kafka离线安装 - ZooKeeper开机自启
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (转)平衡树
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net CHARTING图表控件下载地址
  • .Net Core 微服务之Consul(三)-KV存储分布式锁
  • .Net MVC4 上传大文件,并保存表单
  • .net SqlSugarHelper
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .Net 基于.Net8开发的一个Asp.Net Core Webapi小型易用框架