JVM入门——运行时数据区
jdk1.7.0_79

这张图我相信基本上对JVM有点接触的都应该很熟悉,可以说这是JVM入门的第一课。其中的“堆”和“虚拟机栈(栈)”更是耳熟能详。下面将围绕这张图对JVM的运行时数据区做一个简单介绍。
程序计数器(Program Counter Register)
这和计算机操作系统中的程序计数器类似,在计算机操作系统中程序计数器表示这个进程要执行的下个指令的地址,对于JVM中的程序计数器可以看做是当前线程所执行的字节码的行号指示器,每个线程都有一个程序计数器(这很好理解,每个线程都有在执行任务,如果线程切换后要能保证能恢复到正确的位置),重要的一点——程序计数器,这是JVM规范中唯一一个没有规定会导致OutOfMemory(内存溢出,下文简称OOM)的区域。换句话上图中的其余4个区域,都有可能导致OOM。
☆虚拟机栈(Java Virtual Machine Stacks)
这块内存区域就是我们常常说的“栈”,我们所熟知的是它用于存放变量,也就是说例如:
int i = 0;
虚拟机栈内存就会用4个字节来存储i变量。对于变量的内存空间是一开始就能确定的(对于引用型变量,它当然存储的就是一个地址引用,其大小也是固定),所以这块内存区域在编译器就能够确定下来,这块区域可能会抛出StackOverflowError或者OOM错误。设置JVM参数”-Xss228k”(栈大小为228k)。
package com.jvm; /**
* -Xss228k,虚拟机栈大小为228k
* Created by yulinfeng on 7/11/17.
*/
public class Test {
private static int count = 0; public static void main(String[] args) {
Test test = new Test();
test.test();
} /**
* 递归调用
*/
private void test() {
try {
count++;
test();
} catch (Throwable e) { //Exception已经捕获不了JVM抛出的StackOverflowError
System.out.println("递归调用次数" + count);
e.printStackTrace();
}
}
}
这是一段没有终止条件的递归,执行结果如下图所示,JVM抛出StackOverflowError表示线程请求的栈深度大于JVM所允许的深度。

对于单线程情况下,无论如何抛出的都是StackOverflowError。如果要抛出OOM异常,导致的原因是不断地在创建线程,直到将内存消耗殆尽。
JVM的内存由堆内存 + 方法区内存 + 剩余内存,也就是剩余内存=操作系统分配给JVM的内存 - 堆内存 - 方法区内存。-Xss设置的是每个线程的栈容量,也就是说可以创建的线程数量 = 剩余内存 / 栈内存。此时如果栈内存越大,可以创建的线程数量就少,就容易出现OOM;如果栈内存越小,可以创建的线程数量就多,就不容易出现OOM。
要避免这种情况最好就是减少堆内存+方法区内存,或者适当减少栈内存。对于栈内存的配置,一般采用默认值1M,或者采用64位操作系统以及64位的JVM。
本地方法栈(Native Method Stack)
本地方法栈和虚拟机栈类似,不同的是虚拟机栈服务的是Java方法,而本地方法栈服务的是Native方法。在HotSpot虚拟机实现中是把本地方法栈和虚拟机栈合二为一的,同理它也会抛出StackOverflowError和OOM异常。
☆Java堆(Java Heap)
对于堆,Java程序员都知道对象实例以及数组内存都要在堆上分配。堆不再被线程所独有而是共享的一块区域,它的确是用来存放对象实例,也是垃圾回收GC的主要区域。实际上它还能细分为:新生代(Young Generation)、老年代(Old Generation)。对于新生代又分为Eden空间、From Survivor空间、To Survivor空间。至于为什么这么分,这涉及JVM的垃圾回收机制,在这里不做叙述。堆同样会抛出OOM异常,下面例子设置JVM参数” -Xms20M -Xmx20M“(前者表示初始堆大小20M,后者表示最大堆大小20M)。
package com.jvm; import java.util.ArrayList;
import java.util.List; /**
* -Xms20M -Xmx20M 堆初始大小20M 堆最大大小20M
* Created by yulinfeng on 7/11/17.
*/
public class Test { public static void main(String[] args) {
List<Test> list = new ArrayList<Test>();
int count = 0;
try {
while (true) {
count++;
list.add(new Test()); //不断创建线程
}
} catch (Throwable e) {
System.out.println("创建实例个数:" + count);
e.printStackTrace();
} }
}
执行的结果可以清楚地看到堆上的内存空间溢出了。

☆方法区(Method Area)
对于JVM的方法区,可能听得最多的是另外一个说法——永久代(Permanent Generation),呼应堆的新生代和老年代。方法区和堆的划分是JVM规范的定义,而不同虚拟机有不同实现,对于Hotspot虚拟机来说,将方法区纳入GC管理范围,这样就不必单独管理方法区的内存,所以就有了”永久代“这么一说。方法区和操作系统进程的正文段(Text Segment)的作用非常类似,它存储的是已被虚拟机加载的类信息、常量(从JDK7开始已经移至堆内存中)、静态变量等数据。现设置JVM参数为”-XX:MaxPermSize=20M”(方法区最大内存为20M)。
package com.jvm; import java.util.ArrayList;
import java.util.List; /**
* -XX:MaxPermSize=20M 方法区最大大小20M
* Created by yulinfeng on 7/11/17.
*/
public class Test { public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern()); //不断创建线程
}
}
}
实际上对于以上代码,在JDK6、JDK7、JDK8运行结果均不一样。原因就在于字符串常量池在JDK6的时候还是存放在方法区(永久代)所以它会抛出OutOfMemoryError:Permanent Space;而JDK7后则将字符串常量池移到了Java堆中,上面的代码不会抛出OOM,若将堆内存改为20M则会抛出OutOfMemoryError:Java heap space;至于JDK8则是纯粹取消了方法区这个概念,取而代之的是”元空间(Metaspace)“,所以在JDK8中虚拟机参数”-XX:MaxPermSize”也就没有了任何意义,取代它的是”-XX:MetaspaceSize“和”-XX:MaxMetaspaceSize”等。
JVM入门——运行时数据区的更多相关文章
- 深入理解JVM(2)——运行时数据区
1.运行时数据区 1.1.程序计数器 记录当前线程正在执行的字节码指令的地址,如果正在执行的是 Native 方法,这个计数器值则为空. 1.2.虚拟机栈 每个 Java 方法在执行的同时会创建一个栈 ...
- jvm理论-运行时数据区
三大流行jvm sun HotSpot ibm j9 BEA JRockit Oracle 会基于HotSpot整合 JRockit. jvm运行时数据区 java虚拟机所管理的内存将会包括以下几个运 ...
- JVM虚拟机-运行时数据区概述
目录 运行时数据区域 总览 概念扫盲 什么是栈帧(Stack Frame) JVM常见出现两种错误 程序计数器 虚拟机栈 结构 局部变量表 方法是如何调用的 本地方法栈 堆 浅堆和深堆 堆的细分 方法 ...
- JVM之运行时数据区
Java虚拟机运行时数据区包括PC寄存器.Java虚拟机栈.Java堆.方法区.本地方法栈.运行时常量池六个部分. 1. PC寄存器 PC寄存器(又叫程序计数器,Program Counter Reg ...
- 【JVM第四篇--运行时数据区】堆
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.堆的概述 JVM的运行时数据区如下: 一个Java程序运行起来对应着一个进程(操 ...
- 【转】Java运行时数据区简介及堆与栈的区别
理解JVM运行时的数据区是Java编程中的进阶部分.我们在开发中都遇到过一个很头疼的问题就是OutOfMemoryError(内存溢出错误),但是如果我们了解JVM的内部实现和其运行时的数据区的工作机 ...
- Java 运行时数据区
写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...
- 面试常问的 Java 虚拟机运行时数据区
写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...
- Java 虚拟机运行时数据区
写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...
随机推荐
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...
- vue-动手做个选择城市
查看完整的代码请到 我的github地址 https://github.com/qianyinghuanmie/vue2.0-demos 一.结果展示 二.前期准备: 1.引入汉字转拼音的插件, ...
- hdu4081
hdu4081 题意 给出n个点坐标,每个点有权值,要求得到一颗生成树,且其中有一条道路修建不需要花费,但是要求这条道路所连接的两点的权值之和除以剩下所有道路的距离花费最大. 分析 首先求最小生成树, ...
- poj3207
poj3207 题意 平面上,一个圆,圆的边上按顺时针放着n个点.现在要连m条边, 比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接. 给你的信息中,每个点最多只会连接的一条边.问能不能 ...
- leetcode算法题(JavaScript实现)
题外话 刷了一段时间的codewars的JavaScript题目之后,它给我最大的感受就是,会帮助你迅速的提升你希望练习的语言的API的熟悉程度,Array对象.String对象等原生方法,构造函数. ...
- 基于docker的 Hyperledger Fabric 多机环境搭建(上)
环境:ubuntu 16.04 Docker 17.04.0-ce go 1.7.4 consoul v0.8.0.4 ======================================= ...
- Grunt压缩图片
今天我们来说一下用Grunt来压缩图片和JS吧! 首先要安装插件: 这是压缩图片的; npm install --save-dev gulp-imagemin 这是压缩JS的: npm install ...
- Shiro眼皮下玩ajax,玩出302 Found(实践得经验)
2017/06/14这一天,是我玩Shiro安全框架最刻骨铭心的一天.因为Shiro今天给我深深的补了一刀,在这儿我也给各位补一刀吧,其实问题很简单,解决方式也极其简单,只是给各位分享一下这个错误,纯 ...
- SQL储存过程
基本语法 创建存储过程 create procedure sp_name @[参数名] [类型],@[参数名] [类型] as begin ......... end 以上格式还可以简写成: crea ...
- js 动态 key value
问题描述: 如果var mykey = 'a'; 我们试图 a = {mykey: 'b'} 来达到 a = { 'a': 'b'}的效果,但是mykey直接被作为键值,无法达到要求 解决办法: a ...