秦怀杂货店

General Store

  • 首页
  • 文章归档
  • 标签
  • 分类
  • 关于页面

  • 搜索
随便聊聊 数据结构 小游戏 数据库 Docker Springboot 系统设计 雪花算法 分布式 海量ip 最长回文子串 算法 面试题 线程池 多线程 线程 java学习 布隆过滤器 github 架构设计 docsify Git JVM LeetCode 杂货思考 设计模式 Lambda native isAssignableFrom 反射 剑指Offer mybatis SPI JDBC 编程工具 Java基础 集合

JVM笔记【1】-- 运行时数据区

发表于 2020-12-26 | 分类于 虚拟机 | 0 | 阅读次数 308
  • (一)java内存区域管理
    • 1.1 程序计数器
    • 1.2 虚拟机栈
    • 1.3 本地方法栈
    • 1.4 java堆
    • 1.5 方法区
      • 1.5.1 运行时常量池
  • (二)直接内存

(一)java内存区域管理

C/C++每一个new操作都需要自己去delete/free,而java里面有虚拟机自动管理内存,不容易出现内存泄漏或者溢出的问题,但是不容易出现不代表不出现,了解虚拟机怎么使用和管理内存是十分重要的是,对程序优化或者问题排查有帮助。

运行时区域主要分为:

  • 线程私有:
    • 程序计数器:Program Count Register,线程私有,没有垃圾回收
    • 虚拟机栈:VM Stack,线程私有,没有垃圾回收
    • 本地方法栈:Native Method Stack,线程私有,没有垃圾回收
  • 线程共享:
    • 方法区:Method Area,以HotSpot为例,JDK1.8后元空间取代方法区,有垃圾回收。
    • 堆:Heap,垃圾回收最重要的地方。

image-20201222221827719

1.1 程序计数器

空间很小,当前线程执行的字节码的行号指示器(线程独有,指示当前执行到哪,下一步需要执行哪一个字节码),分支,循环,跳转,异常处理,线程恢复都需要依赖它。
线程私有:java多线程其实是线程轮流切换并分配处理器执行时间的方式实现,一个核一个具体的时间点,只会执行一个线程的指令。线程切换需要保存和恢复正确的执行位置(保护和恢复现场),所以不同的线程需要不同的程序计数器。

  • 执行java方法时,程序计数器记录的是正在执行的字节码指令地址
  • 执行Native方法,程序计数器为空

唯一一个没有规定任何OutOfMemory的区域,也没有GC(垃圾回收)。

1.2 虚拟机栈

线程私有,生命周期和线程一样,主要是记录该线程Java方法执行的内存模型。虚拟机栈里面放着好多栈帧。注意虚拟机栈,对应是Java方法,不包括本地方法。
一个Java方法执行会创建一个栈帧,一个栈帧主要存储:

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法出口
    每一个方法调用的时候,就相当于将一个栈帧放到虚拟机栈中(入栈),方法执行完成的时候,就是对应着将该栈帧从虚拟机栈中弹出(出栈)。

每一个线程有一个自己的虚拟机栈,这样就不会混起来,如果不是线程独立的话,会造成调用混乱。

大家平时说的java内存分为堆和栈,其实就是为了简便的不太严谨的说法,他们说的栈一般是指虚拟机栈,或者虚拟机栈里面的局部变量表。

局部变量表一般存放着以下数据:

  • 基本数据类型(boolean,byte,char,short,int,float,long,double)
  • 对象引用(reference类型,不一定是对象本身,可能是一个对象起始地址的引用指针,或者一个代表对象的句柄,或者与对象相关的位置)
  • returAddress(指向了一条字节码指令的地址)

局部变量表内存大小编译期间确定,运行期间不会变化。空间衡量我们叫Slot(局部变量空间)。64位的long和double会占用2个Slot,其他的数据类型占用1个Slot。

异常:

  • StackOverflowError:线程请求的栈深度大于虚拟机允许的深度
  • OutOfMemoryError:内存不足

1.3 本地方法栈

和虚拟机栈类似,对应本地方法,Native,虚拟机规范允许语言,使用方式和数据结构不同,有些可能将虚拟机栈和本地方法栈合并。
异常与虚拟机栈一致:

  • StackOverflowError:线程请求的栈深度大于虚拟机允许的深度
  • OutOfMemoryError:内存不足

1.4 java堆

堆是内存管理最大的一块,线程共享。

虚拟机规范中说,所有的对象实例和数组都要在堆上分配。但是实际上不是所有的对象都在堆上分配,这个和JIT编译器的发展和逃逸分析技术相关。Why?
// TODO
堆的细分:新生代,老年代,再细分有Eden,From survivor,To survivor等。

堆中也有可能有线程私有的区域,分配缓冲区。

物理上可以不连续,但是逻辑上是连续的。

异常:

  • OutOfMemoryError:内存不足

1.5 方法区

名为非堆,但是实际和堆一样,是线程共享的区域,主要存贮以下信息:

  • 已被虚拟机加载的类信息
  • 常量
  • 静态变量
  • 即时编译器编译后的代码

方法区不等于永久代,指示Hotspot虚拟机将GC分代收集拓展到方法区,也就是用永久代实现了方法区,而其他的虚拟机不一定,不是固定的。JDK1.7将永久代的字符串常量移出了。

方法区回收垃圾的效果不是很好,可以选择不回收,虚拟机可以决定,当然也可能发生内存泄漏。
异常:

  • OutOfMemoryError:内存分配异常

1.5.1 运行时常量池

运行时常量池时方法区的一部分,但是不是全部,Class文件主要包括:

  • 类的版本
  • 字段
  • 方法
  • 接口
  • 常量池,存放编译产生的字面量和符号引用,一般除了描述Class文件的符号引用,还有直接引用也在里面。是动态的,运行时可以产生,比如String.intern()方法。

异常:

  • OutOfMemoryError:内存分配异常

(二)直接内存

不是虚拟机运行时数据区,也不是规范规定的区域,但是使用频繁且可能会有OutOfMemoryError:内存分配异常出现。
比如,NIO(1.4)基于Channel与Buffer的I/O,可以用Native函数直接分配堆外内存,通过存储在Java堆中的DirectByteBuffer对象作为引用来操作,提高性能,不需要Java堆和Native堆都来回复制数据。

直接内存受物理的内存,或者处理器寻址空间之类的限制。

本文系JVM学习相关笔记,整理来自周志明老师的《深入理解Java虚拟机》,无比钦佩,强烈推荐!

【作者简介】:
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。这个世界希望一切都很快,更快,但是我希望自己能走好每一步,写好每一篇文章,期待和你们一起交流。

此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者核实删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~

  • 本文作者: 秦怀杂货店
  • 本文链接: http://aphysia.cn/archives/jvm01
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 随便聊聊 # 数据结构 # 小游戏 # 数据库 # Docker # Springboot # 系统设计 # 雪花算法 # 分布式 # 海量ip # 最长回文子串 # 算法 # 面试题 # 线程池 # 多线程 # 线程 # java学习 # 布隆过滤器 # github # 架构设计 # docsify # Git # JVM # LeetCode # 杂货思考 # 设计模式 # Lambda # native # isAssignableFrom # 反射 # 剑指Offer # mybatis # SPI # JDBC # 编程工具 # Java基础 # 集合
剑指Offer【32】-- 将数组排成最小的数
Mybatis【12】-- Mybatis多条件怎么查询?
  • 文章目录
  • 站点概览
秦怀杂货店

秦怀杂货店

纵然缓慢,驰而不息。

145 日志
19 分类
37 标签
Github E-mail
Creative Commons
0%
© 2022 秦怀杂货店
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4