文章目录

  • jvm整体架构
    • 1.java运行过程
    • 2.jvm模型
  • 运行数据区
    • 1.程序计数器
      • 1.1 概述
      • 1.2 溢出异常
      • 1.3 案例
    • 2.虚拟机栈
      • 2.1 概述
      • 2.2 溢出异常
    • 3.本地方法栈
      • 3.1 概述
      • 3.2 溢出异常
    • 4.堆
      • 4.1 概述
      • 4.2 jdk1.7
      • 4.3 jdk1.8
      • 4.4 溢出异常
    • 5.方法区
      • 5.1 概述
      • 5.2 溢出异常
      • 5.3 案例:1.6/1.8 方法区溢出
    • 6.一个案例
    • 7.归纳总结

jvm整体架构

1.java运行过程

1.源码编译:通过Java源码编译器将Java代码编译成JVM字节码(.class文件)

2.类加载:通过ClassLoader及其子类来完成JVM的类加载

3.类执行:字节码被装入内存,进入JVM虚拟机,被解释器解释执行

2.jvm模型

JVM虚拟机中主要是由三部分构成,分别是类加载子系统、运行时数据区、执行引擎。

类加载子系统

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

运行时数据区

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。

这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。

执行引擎

执行引擎用于执行JVM字节码指令,主要有两种方式,分别是解释执行和编译执行,区别在于,解释执行是在执行时翻译成虚拟机指令执行,而编译执行是在执行之前先进行编译再执行。

解释执行启动快,执行效率低。编译执行,启动慢,执行效率高。

垃圾回收器就是自动管理运行数据区的内存,将无用的内存占用进行清除,释放内存资源。

本地方法库、本地库接口

在jdk的底层中,有一些实现是需要调用本地方法完成的(使用c或c++写的方法),就是通过本地库接口调用完成的。比如:System.currentTimeMillis()方法。

运行数据区

1)运行时数据区的位置

运行时数据区是jvm中最为重要的部分,执行引擎频繁操作的就是它。类的初始化,以及后面我们讲的对象空间的分配、垃圾的回收都是在这块区域发生的。

2)区域划分

根据《Java虚拟机规范》中的规定,在运行时数据区将内存细分为几个部分

线程私有的:Java虚拟机栈(Java Virtual Machine Stack)、程序计数器(Program Counter Register)、本地方法栈(Native Method Stacks)

共享的:方法区(Method Area)、Java堆区(Java Heap)

1.程序计数器

1.1 概述

程序计数器(Program Counter Register)

  • 每个线程一个。是一块较小的内存空间,它表示当前线程执行的字节码指令的地址。

  • 字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令,所以整个程序无论是分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

  • 由于线程是多条并行执行的,互相之间执行到哪条指令是不一样的,所以每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

  • 如果是native方法,这里为空

1.2 溢出异常

没有!

在虚拟机规范中,没有对这块区域设定内存溢出规范,也是唯一一个不会溢出的区域

1.3 案例

因为它不会溢出,所以我们没有办法给它造一个,但是从class类上可以找到痕迹。

回顾上面javap的反汇编,其中code所对应的编号就可以理解为计数器中所记录的执行编号。

2.虚拟机栈

2.1 概述

  • 也是线程私有的!生命周期与线程相同。
  • 它描述的是Java方法执行的当前线程的内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

2.2 溢出异常

1)栈深度超出设定

如果是创建的栈的深度大于虚拟机允许的深度,抛出

Exception in thread “main” java.lang.StackOverflowError

2)内存申请不足

如果栈允许内存扩展,但是内存申请不够的时候,抛出 OutOfMemoryError

注意!这一点和具体的虚拟机有关,hotspot虚拟机并不支持栈空间扩展,所以单线程环境下,一个线程创建时,分配给它固定大小的一个栈,在这个固定栈空间上不会出现再去扩容申请内存的情况,也就不会遇到申请不到一说,只会因为深度问题超出固定空间造成上面的StackOverflowError

如果换成多线程,毫无节制的创建线程,还是有可能造成OutOfMemoryError。但是这个和Xss栈空间大小无关。是因为线程个数太多,栈的个数太多,导致系统分配给jvm进程的物理内存被吃光。

这时候虚拟机会附带相关的提示:

Exception in thread “main” java.lang.OutOfMemoryError: unable to create native thread

ps: 每个线程默认分配1M空间(64位linux,hotspot环境)

疑问:是不是改小Xss的值就可以得到栈空间溢出呢?

答:根据上面的分析,hotspot下不可以,还是会抛出StackOverflowError,无非深度更小了。

3.本地方法栈

3.1 概述

  • 本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点
  • 不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法
  • 虚拟机规范里对这块所用的语言、数据结构、没有强制规定,虚拟机可以自由实现它
  • 甚至,hotspot把它和虚拟机栈合并成了1个

3.2 溢出异常

和虚拟机栈一样,也是两个:

如果是创建的栈的深度大于虚拟机允许的深度,抛出 StackOverFlowError

内存申请不够的时候,抛出 OutOfMemoryError

4.堆

4.1 概述

与上面的3个不同,堆是所有线程共享的!所谓的线程安全不安全也是出自这里。

在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。

需要注意的是,《Java虚拟机规范》并没有对堆进行细致的划分,所以对于堆的讲解要基于具体的虚拟机,我们以使用最多的HotSpot虚拟机为例。

Java堆是垃圾收集器管理的内存区域,因此它也被称作“GC堆”,这就是我们做JVM调优的重点区域部分。

4.2 jdk1.7

jvm的内存模型在1.7和1.8有较大的区别,虽然1.7目前使用的较少了,但是我们也是需要对1.7的内存模型有所了解,所以接下里,我们将先学习1.7再学习1.8的内存模型。

  • Young 年轻区(代)

    Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区

    其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用

    在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到下面的Tenured区间。

  • Tenured 年老区

    Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

  • Perm 永久区

    现在已经成为历史,Perm代主要保存类信息,class,method,filed等对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。另外一种可能是创建了大批量的jsp文件,造成类信息超出perm的上限而溢出。这种重启也解决不了。只能调大空间。

4.3 jdk1.8

由上图可以看出,jdk1.8的内存模型是由2部分组成,年轻代 + 年老代。永久代被干掉,换成了Metaspace(元数据空间)

年轻代:Eden + 2*Survivor (不变)

年老代:OldGen (不变)

元空间:原来的perm区 (重点!)

需要特别说明的是:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间中,这也是与1.7的永久代最大的区别所在。

4.4 溢出异常

内存不足时,抛出

java.lang.OutOfMemoryError: Java heap space

5.方法区

5.1 概述

同样,线程共享的。

它主要用来存储类的信息、类里定义的常量、静态变量、编译器编译后的代码缓存。

注意!方法区在虚拟机规范里这是一个逻辑概念,它具体放在那个区域里没有严格的规定。

所以,hotspot 1.7- 将它放在了堆的永久代里,1.8+单独开辟了一块叫metaspace来存放一部分内容(不是全部!定义的类对象在堆里)

具体方法区主要存什么东西呢?粗略的分,可以划分为两类:

  • 类信息:主要指类相关的版本、字段、方法、接口描述、引用等

  • 运行时常量池:编译阶段生成的常量与符号引用、运行时加入的动态变量

    (常量池里的类变量,如对象或字符串,比较特殊,1.6和1.8位置不同,下面会讲到)

小提示:

这里经常会跟上面堆里的永久代混为一谈,实际上这是两码事

永久代是hotspot在1.7及之前才有的设计,1.8+,以及其他虚拟机并不存在这个东西。

可以说,永久代是1.7的hotspot偷懒的结果,他在堆里划分了一块来实现方法区的功能,叫永久代。因为这样可以借助堆的垃圾回收来管理方法区的内存,而不用单独为方法区再去编写内存管理程序。

同时代的其他虚拟机,如J9,Jrockit等,没有这个概念。后来hotspot认识到,永久代来做这件事不是一个好主意。1.7已经从永久代拿走了一部分数据,直到1.8+彻底去掉了永久代,方法区大部分被移到了metaspace(再强调一下,不是全部!)

结论:

方法区是一定存在的,这是虚拟机规定的,但是是个逻辑概念,在哪里虚拟机自己去决定

而永久代不一定存在,已成为历史

5.2 溢出异常

1.6:OutOfMemoryError: PermGen space

1.8:OutOfMemoryError: Metaspace

5.3 案例:1.6/1.8 方法区溢出

在1.6里,字符串常量是运行时常量池的一部分,也就是归属于方法区,放在了永久代里。

jdk1.8以后,字符串常量池被移到了堆空间,和其他对象一样,接受堆的控制。

其他的运行时的类信息、基本数据类型等在元空间。

结论:

jdk8引入元空间来存储方法区后,内存溢出的风险比历史版本小多了,但是在类超出控制的时候,依然会打爆方法区

6.一个案例

为便于大家理解和记忆,下面我们用一个案例,把上面各个区串通起来。

假设有个Bootstrap的类,执行main方法。在jvm里,它从class文件到跑起来,大致经过如下步骤:

  1. 首先JVM会先将这个Bootstrap.class 信息加载到内存中的方法区
  2. 接着,主线程开辟一块内存空间,准备好程序计数器pc,虚拟机栈、本地方法栈
  3. 然后,JVM会在Heap堆上为Bootstrap.class 创建一个Bootstrap.class 的类实例
  4. JVM开始执行main方法,这时在虚拟机栈里为main方法创建一个栈帧
  5. main方法在执行的过程之中,调用了greeting方法,则JVM会为greeting方法再创建一个栈帧,推到虚拟机栈顶,在main的上面,每次只有一个栈帧处于活动状态,当前为greeting
  6. 当greeting方法运行完成后,则greeting方法出栈,当前活动帧指向main,方法继续往下运行

7.归纳总结

1)独享/共享的角度:

  • 独享:程序计数器、虚拟机栈、本地方法栈
  • 共享:堆、方法区

2)error的角度:

  • 程序计数器:不会溢出,比较特殊,其他都会
  • 两个栈:可能会发生两种溢出,一是深度超了,报StackOverflowError,空间不足:OutOfMemoryError
  • 堆:只会在空间不足时,报OutOfMemoryError,会提示heapSpace
  • 方法区:空间不足时,报OutOfMemoryError,提示不同,1.6是permspace,1.8是元空间,和它在什么地方有关

3)归属:

  • 计数器、虚拟机栈、本地方法栈:线程创建必须申请配套,真正的物理空间
  • 堆:真正的物理空间,但是内部结构的划分有变动,1.6有永久代,1.8被干掉
  • 方法区:最没归属感的一块,原因就是它是一个逻辑概念。1.6被放在了堆的永久代,1.8被拆分,一部分在元空间,一部分(方法区的运行时常量池里面的类对象,包括字符串常量,被设计放在了堆里)
  • 直接内存:这块实际上不属于运行时数据区的一部分,而是直接操作物理内存。在nio操作里DirectByteBuffer类可以对native操作,避免流在堆内外的拷贝。我们下一步的调优不会涉及到它,了解即可。

博学谷学习记录】超强总结,用心分享 | 架构师 JVM内核调优学习总结相关推荐

  1. [博学谷学习记录]超强总结,用心分享|架构 敏捷 - 开发管理之道

    提示:学习笔记 欢迎指点 文章目录 1.敏捷开发思想之道 2.面向对象开发之道 3.敏捷学习之道 1.敏捷开发思想之道 一名敏捷开发者,敏捷思想的掌握自然首当其冲.在敏捷开发实施的过程中,我们虽然不是 ...

  2. [博学谷学习记录]超强总结,用心分享|架构 Nacos入门

    提示:学习笔记 欢迎指点 文章目录 前言 一.Nacos安装 二.Nacos服务注册与发现 1.服务提供者Provider 2.服务消费者Consumer 三.Nacos作为配置中心 前言 Nacos ...

  3. [博学谷学习记录]超强总结,用心分享|第07节 常用的API-----笔记篇

    目录 1.API 1.1 API概述-帮助文档的使用 1.2 键盘录入字符串 2. String类 2.1 String概述 2.2 String类的构造方法 2.4 创建字符串对象的区别对比 2.5 ...

  4. {博学谷学习记录} 超强总结,用心分享|狂野架构师-前置互联网架构演变过程

    本章以系统架构,数据架构,两种维度来进行讲解 目录 1 系统架构 1,1 单体架构 1.2 中台战略 2 数据库架构 2,1 单体架构 2.2 主从读写 2.3 分库分表 3 总结 1 系统架构 1, ...

  5. [博学谷学习记录]超强总结,用心分享|第16节 集合续-----笔记篇

    目录 1.HashSet集合 1.1HashSet集合概述和特点[应用] 1.2HashSet集合的基本应用[应用] 1.3哈希值[理解] 1.4哈希表结构[理解] 1.5HashSet集合存储学生对 ...

  6. 系统架构设计师教程-学习-记录(1)系统架构师知识结构

    系统架构设计师教程-学习-记录(1)系统架构师知识结构 初 记 系统架构师知识结构 系统架构师具备的能力 初 欢迎进qq群交流:546496965 学习<系统架构设计师教程>,初心是为了学 ...

  7. 【博学谷学习记录】超强总结,用心分享 | 架构师 Mybatis源码学习总结

    Mybatis源码学习 文章目录 Mybatis源码学习 一.Mybatis架构设计 二.源码剖析 1.如何解析的全局配置文件 解析配置文件源码流程 2.如何解析的映射配置文件 Select inse ...

  8. 【博学谷学习记录】超强总结,用心分享 | 架构师 Kafka学习总结

    文章目录 概述 Kafka特性 多个生产者和多个消费者 可持久化操作 高吞吐量 可伸缩性 实时性 容错性 Kafka应用场景 消息处理 网站活动追踪 指标分析 日志聚合 流处理 事件采集 Kafka架 ...

  9. 【博学谷学习记录】超强总结,用心分享 | 架构师 Netty框架学习总结

    文章目录 前言 一.Netty · Netty简介 · Netty核心架构 · Netty中Reactor实现 1.工作流程 2.ChannelPipeline 和 ChannelHandler(开发 ...

最新文章

  1. Java项目:在线水果商城系统(java+JSP+Spring+SpringMVC +MyBatis+html+mysql)
  2. tablednd保存 php,jqgrid中使用tableDnd插件,jqgridtablednd插件
  3. 程序员如何才能提高自己?通过一次重构代码讲解自己的感受【有代码比较】...
  4. collection集合 多少钱_Java 集合(2)-- Iterator接口源码超级详细解析
  5. 用HttpListener做web服务器,简单解析post方式过来的参数、上传的文件
  6. 数据结构与算法之栈入门题目
  7. java gzip 多个文件_Java Zip多文件压缩和 GZIP压缩
  8. Android之ndk之gdb调试
  9. openwrt安装蒲公英_网速不给力?双宽带叠加,立马消除卡顿
  10. 微信支付get_brand_wcpay_request:fail
  11. android go解析json,Go 关于Json通用解析
  12. SharePoint 2010 technology stack
  13. s400x ugee 驱动_联想_ThinkPad|ThinkCentre|ThinkStation服务与驱动下载_常见问题
  14. BZOJ3533: [Sdoi2014]向量集
  15. 雷霆战机服务器维护公告,雷霆战机停服公告 4月11日服务器维护
  16. 流程表结构设计第二版
  17. dry的原理_4、干法蚀刻(dry etch)原理介绍
  18. 对接微信支付之网页支付详解
  19. 【原创:C++编写库】自己的库
  20. win7下32位eclipse启动错误

热门文章

  1. 加速化学制品制造业转型升级,S2B2C供应链平台使用电子合同降本增效
  2. matlab linspace多个区间,linspace分为100区间却得到200个区间
  3. python循环语句打印三角形_三角形最新:Python利用for循环打印星号三角形的案例_爱安网 LoveAn.com...
  4. watch watch watch the video! I got almost addicted. Oh what a fuck!!!!
  5. Idea设置web默认浏览器
  6. 儿童摄影技巧:暗调婴儿摄影构思分…
  7. 工业互联网也迈入新的发展阶段 AI助力构建新型制造体系
  8. 网页千色字 v3.5.200706 轻装版 免费
  9. Ie6 Ie7 常见 兼容问题解决方案
  10. 关于windows程序显示鼠标所在坐标