基本上,业内对 JVM 的理解就源自于一本书——

《深入理解 JAVA 虚拟机》

谁没事就真的上手调优啊?基本所有的中文博客要么是理解了这本书的小结要么是错误地理解了这本书的小结。


整理一下,大概分为这么几块,

  • Java 内存
  • 垃圾处理
  • 类结构、类加载
  • 调优工具

以原书为准。讲道理,目前所有人的信息来源都是那本书。这本书只涉及 1.7 版本。


Java 内存

Java 程序员没有权力控制内存,只有虚拟机才可以。运行时内存被分为如下模块,

程序计数器 : 线程私有,独立计数。

Java 虚拟机栈 : 线程私有,每个方法执行时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接等。一个方法的调用到完成就是一个栈帧的入栈到出栈。一般来说,可以粗浅地把 Java 内存分为堆和栈,这里的栈指的就是虚拟机栈的局部变量表。如果栈帧深度过高,则抛出 StackOverflowError。

还存在一个本地方法栈。本地方法栈和虚拟机栈非常类似,只不过前者执行 Java 方法,后者执行虚拟机的本地方法,如 C++。有些虚拟机实现时二者合一。

然后就是堆(Heap) 。堆最大且全线程共享。大多数时候这里唯一要做的事就是存放实例。堆也是 GC 垃圾处理的主要区域,也称 GC 堆。

最后一个就是方法区 Method Area。一样是全线程共享。它存放类信息、常量、静态变量,编译后代码等。方法区是堆的逻辑处理模块,也称非堆 Non-Heap。这个区看起来是不可修改的,其实并不。String 的 intern 方法可以在运行时将常量放置其中。放置常量的地方叫运行时常量池,是方法区的一部分。

(JDK 1.4 中加入的 NIO 可以绕过堆直接分配内存,这是为了提高性能,避免 Java 堆和外部数据频繁地来回地复制数据。)


new 这个非常常用的关键字开始研究虚拟机是如何工作的。

具体来说,虚拟机首先去看常量池有没有这个类,没有则去加载。这个类加载完了,下一步计算这个类的实例要多大的空间,然后去堆中划分出一块来。

当这个空间分配到了,先将这块空间初始化为零。然后虚拟机在对象头里标识这个实例是什么类,以及哈希码等。到此,虚拟机的工作结束了。这个时候 Java 程序开始执行 init 方法了,在这里赋初始值。


上述是虚拟机规范。可具体实现的时还是有相当多不一样的地方。说明一下牛逼的 HotSpot 虚拟机是怎么工作的。

首先对象分为三块,

  • 对象头 Header。一部分是运行时数据,成为Mark Word,具体包含GC年代、锁信息等。另一部分指向元数据,即这个对象是哪一个类的实例,如果是数组还包括数组长度等。
  • 实例数据 Instance Data。字段。
  • 填充 Padding。补齐为 8 字节对齐。

说一下 JVM 的内存结构?

虚拟机栈,栈帧和局部变量表
本地方法栈
方法区

方法区
程序计数器

怎么选择垃圾回收器?

1、单CPU或者小内存,单机程序 — -XX:+UseSerialGC
2、多CPU,需要大吞吐量,如后台计算型应用
-XX:+UseParallelGC + -XX:+UseParallelOldGC
3、多CPU,追求低停顿时间,快速响应如互联网应用
-XX:+UseParNewGC + -XX:+UseConcMarkSweepGC

什么时候进行 Full GC 呢?System.gc()之后 会不会立刻 Full GC ?

System.gc()建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC。
1. 老年代空间不足
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2. 持久代 Permanet Generation空间满
Permanet Generation中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:
java.lang.OutOfMemoryError: PermGen space
为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。
3. CMS GC 时出现promotion failed和concurrent mode failure
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。

CMS 收集器如何工作的?

初始化
并发
修正并发错误
清除
初始标记 CMS initial mark
并发标记 CMS concurrnet mark
重新标记 CMS remark
并发清除 CMS concurrnet sweep
第一阶段和第三阶段需要 Stop the World。但是二四阶段可以和用户线程并发。可以注意的是 CMS 无法与 Parallel Scavenge 配合。
初始标记仅仅是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因为用户程序继续运作而导致的标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短,整个过程中耗时最长的并发标记和并发清除可以和用户线程一起工作,所以CMS收集器的内存回收过程是与用户线程一起并发执行的。

详细谈谈G1的优点, G1 可预测停顿时间,是怎么做到的?

G1将堆重分为多个大小相等的 Region 。G1 跟踪每个 Region 里面的的垃圾,并维持一个垃圾优先级列表,每次回收排第一的 Region 。

遇到过线上事故吗? 怎么查问题?

java/bin 目录下的工具

简述内存运行时数据区

程序计数器(Program Counter Registers)
用于记录当前线程的正在执行的字节码指令位置。由于虚拟机的多线程是切换线程并分配cpu执行时间的方式实现的,不同线程的执行位置都需要记录下来,因此程序计数器是线程私有的。这个区域是唯一一个不会抛出任何异常的区域。
虚拟机栈(Java Threads)
虚拟机栈是线程私有的。虚拟机栈是java方法执行的内存结构,虚拟机会在每个java方法执行时创建一个“栈桢”,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。当方法执行完毕时,该栈桢会从虚拟机栈中出栈。其中局部变量表包含基本数据类型和对象引用;在java虚拟机规范中,对这个区域规定了两种异常状态:如果线程请求的栈的深度大于虚拟机允许的深度,将抛出StackOverFlowError异常(对于单个线程来说的栈溢出):
本地方法栈(Native Internal Threads)
本地方法栈是线程私有的。本地方法栈为虚拟机使用的Native方法(本地方法)提供服务,这个Native方法指得就是Java程序调用了非Java代码,算是一种引入其它语言程序的接口。和虚拟机栈类似,本地方法栈也会抛出StackOverFlowException和OutOfMemoryException异常。
方法区(Method Area)
方法区是线程共享的内存区域,用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译的代码等数据。通常被开发人员成为“永久代”。这个区域的内存回收的目标就是针对常量池的回收和对类型的卸载,也是较为难处理的部分。方法区溢出时会抛出OutOfMemoryException异常。
堆(Heap)
堆是java虚拟机中内存中最大的一块,被所有线程共享的一块内存区域,在虚拟机创建时创建。作用就是存放对象实例,所有的对象的实例都需要在这里分配内存。几乎所有的对象实例和对象数组都需要在堆上分配。是java虚拟机内存回收的管理的重要区域,因此也被称为“GC”堆,可以被分为新生代和老年代。新生代又由Eden空间、From Survivor空间、To Survivor空间组成。如果堆中没有内存完成实例分配,并且堆也无法扩展时,则抛出OutOfMemoryException异常。

StackOverFlow 和 OOM 的区别

栈溢出,内存溢出

JVM 启动参数

JVM标准参数(-)
JVM的标准参数都是以”-“开头,通过输入”java -help”或者”java -?
JVM非标准参数(-X)
非标准参数又称为扩展参数 通过”java -X”可以输出非标准参数列表
JVM非Stable参数(-XX)
Java 6(update 21oder 21之后)版本, HotSpot JVM 提供给了两个新的参数,在JVM启动后,在命令行中可以输出所有XX参数和值。
-Xms
指定jvm堆的初始大小,默认为物理内存的1/64,最小为1M;可以指定单位,比如k、m,若不指定,则默认为字节。
-Xmx
指定jvm堆的最大值,默认为物理内存的1/4或者1G,最小为2M;单位与-Xms一致。
-XX:+UseThreadPriorities 启用本地线程优先级
行为参数(Behavioral Options):用于改变jvm的一些基础行为;
性能调优(Performance Tuning):用于jvm的性能调优;
调试参数(Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;

JVM 垃圾回收策略

标志-清除算法 Mark-Sweep 算法。这个算法分为两阶段,先标记处所有需要回收的对象,然后统一清除。这个方法会产生大量的内存碎片,性能很低。
复制算法 Coping。将内存均分为两块,每次只使用一块进行内存分配。当被使用块被标记后,将剩余活着的对象直接复制到另一块,原先的一块清空。这个方法简单粗暴,但是可利用内存只有一半。
标记-整理算法 Mark-Conpact算法。和标志-清除算法大致相同,区别是在清除结束了后会把对象往一侧移动,挤压内存碎片留下的空隙。

主内存和工作内存之间数据如何传递

read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

如果线上有一个 Java 服务器 cpu 100%,如何解决?

jmap 可能不行,因为此时内存可能极多。
top 指令实时查看所有线程状态,或通过 ps 查看当前 Java 线程 pid。
jstack pid 打印当前堆栈信息。

卸载虚拟机出现用户已存在的错误_极限 JVM (1) 虚拟机规范相关推荐

  1. 卸载虚拟机出现用户已存在的错误_用虚拟机安装360全家桶是什么体验

    废话不说,直接开始 win7旗舰版x64 8线程,模拟7700k 保险点,给个12G运存 128G预分配,硬盘文件丢在PM841上,PM841什么水平在座各位心里应该有数 开始启动安装啦 先关掉UAC ...

  2. 卸载虚拟机出现用户已存在的错误_BATJ面试必会|Jvm 虚拟机篇

    每天给你诚意满满的干货 作者:CyC2018出自:https://github.com/CyC2018 目录 一.运行时数据区域 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 ...

  3. ora 00900 已编译但有错误_技术分享|万万没想到!编译错误竟然还没灭绝???

    CodeWisdom-技术分享 万万没想到!编译错误竟然还没灭绝??? 复旦大学CodeWisdom团队的代码分析和挖掘小组针对开源软件项目持续集成过程中出现的编译错误,进行了大规模的经验研究.该研究 ...

  4. linux+锐龙+段错误,锐龙三代BIOS更新出现BUG,不少用户已中招

    原标题:锐龙三代BIOS更新出现BUG,不少用户已中招 7月20日消息,近日AMD为修复最新发售的第三代锐龙处理器存在的<命运2>游戏和部分版本Linux系统无法运行等问题,向主板厂商发送 ...

  5. linux我安装虚拟机后安装FTP出现如下错误求解决

    linux我安装虚拟机后安装FTP出现如下错误求解决 useradd: user 'www.xxxx.xxx' already exists      linux系统   中间是个域名 我先在我挂载的 ...

  6. matlab打开显示系统错误,win7系统笔记本运行Matlab软件弹出已停止工作错误窗口的解决方法...

    无论谁在使用电脑的时候都可能会发现笔记本运行Matlab软件弹出已停止工作错误窗口的问题,笔记本运行Matlab软件弹出已停止工作错误窗口让用户们很苦恼,这是怎么回事呢,笔记本运行Matlab软件弹出 ...

  7. 检测输入路径是否存在错误_为什么存在用户输入错误

    检测输入路径是否存在错误 Errors are a fact of life when using almost any type of software. Forms are the worst t ...

  8. python输入错误提示再次输入_Python实现用户登录并且输入错误三次后锁定该用户...

    实现用户登录并且输入错误三次后锁定该用户 我的测试环境,win7,python3.5.1 提示输入用户名,和密码 判断是否被锁定 判断用户名和密码是否匹配 输入错误三次,账号被锁定 思路 代码块 na ...

  9. VSCode无法写入用户设置 请打开用户设置并清除错误或警告, 然后重式

    VSCode无法写入用户设置 请打开用户设置并清除错误或警告, 然后重式 问题:晚上学弟问我他的VSCode安装插件时总是显示无法写入用户设置 请打开用户设置并清除错误或警告, 然后重式的警告的问题, ...

最新文章

  1. oracle查询表的索引
  2. IO多路复用概念介绍
  3. 【VS开发】CTimeSpan类
  4. html语言代码游戏,常用html语言代码
  5. ProjectMan是这样炼成的
  6. 语音识别ASR技术通识
  7. mysql配置文件my.cnf详解
  8. android上传字符串到服务器,【图片】【转】通过Android 客户端上传数据到服务器【aide吧】_百度贴吧...
  9. leecode第六十二题(不同路径)
  10. 软件技术架构:通过限流与熔断,打造一个“靠谱”的系统
  11. 人工智能发展简史, 没想到17世纪AI就出现了!
  12. 百度网盘打开显示读写权限不足
  13. 01 牛刀小试【PAT B1012】数字分类
  14. 聚搜-聚合搜索引擎网页模板
  15. 面试官问我SpringBean生命周期,我
  16. 哺乳时宝宝一边吃奶,另一边却自动流出来,这是怎么回事?
  17. python+opencv通过颜色阙值识别黑色飞机,并且输出中心点
  18. 如何高效Debug(又名如何高效解决问题)
  19. gcore 获取程序core dump file 但程序不用退出,gdb 分析core
  20. 世界上最好的图片无损压缩软件

热门文章

  1. Java--日期的使用
  2. 【2018.3.4】实验修正
  3. MySQL 触发器应用案例
  4. 如何在HTML页面中插入百度地图
  5. (用微信扫的静态链接二维码)微信native支付模式官方提供的demo文件中的几个bug修正...
  6. Android开发之Buidler模式初探结合AlertDialog.Builder解说
  7. CCTextFieldTTF 与 5种常用CCMenuItem
  8. 启用关闭数据库归档(ARCHIVELOG)模式
  9. 运营商级网络地址转换(LSN/CGN)方案介绍
  10. 最新公开的华为认证通关秘籍来了