JVM的基本结构及其各部分详解(二)
3.2 栈帧组成之操作数栈
操作数栈是栈帧的主要内容之一,它主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。
操作数栈也是一个先进后出的数据结构,只支持入栈和出栈两种操作,许多java字节码指令都需要通过操作数栈进行参数传递。比如add指令,它就会在操作数栈中弹出两个整数并进行加法计算,计算结果会被入栈,如图:显示了iadd前后操作数栈的变化。

3.3 帧数据区
除了局部变量表和操作数栈,java栈帧还需要一些数据来支持常量池的解析、正常方法返回和异常处理等。大部分java字节码指令需要进行常量池访问,在帧数据区中保留着访问常量池的指针,方便程序访问常量池。
此外,当函数返回或者出现异常时,虚拟机必须恢复调用者函数的栈帧,并让调用者函数继续执行下去。对于异常处理,虚拟机必须有一个异常处理表,方便在发生异常时找到处理异常的代码,因此异常处理表也是帧数据区中重要的一部分,一个典型的异常处理表如下所示:
Exception table:
from to target type
4 16 19 any
19 21 19 any
它表示在字节码偏移量4--16字节可能抛出任意异常,如果抛出异常,则跳转到字节码偏移量19处执行。当方法抛出异常时,虚拟机就会查找类似的异常表来处理,如果无法在异常表中找到合适的处理方法,则会结束当前函数调用,返回调用函数,并在调用函数中抛出相同的异常,并查找调用函数的异常表来进行处理。
3.4 栈上分配
栈上分配是java虚拟机提供的一项优化技术,它的基本思想是,对于那些线程私有的对象(这里指不可能被其他线程访问的对象),可以将他们打散分配到栈上,而不是分配到堆上。分配到栈上的好处是可以在函数调用结束后自行销毁,而不需要垃圾回收器的介入,从而提高系统的性能。
栈上分配的一个技术基础是进行逃逸分析,逃逸分析的目的是判断对象的作用域是否有可能逃逸出函数体。如下代码所示显示了一个逃逸对象:
private static User u;
public static void alloc(){
u = new User();
u.id = 5;
u.name = "jim";
}
对象u是类的成员变量,该字段有可能被任何线程访问,因此属于逃逸对象,而以下对象显示了一个非逃逸对象:
public static void alloc(){
User u = new User();
u.id = 5;
u.name = "jim";
}
在上述代码中,对象User u 以局部变量的形式存在,并且该对象并没有被alloc()函数返回或者出现任何形式的公开,因此它未发生逃逸,所以对于这种情况,虚拟机就有可能将User u 分配在栈上,而不是在堆上。
对于大量的零散小对象,栈上分配提供了一种良好的对象分配优化策略,栈上分配速度快,并且可以有效避免垃圾回收带来的负面影响。但由于栈和堆空间相比,栈空间较小,因此对于大对象无法也不适合在栈上分配。
实例1:测试非逃逸对象的分配空间位置
package com.jvm;
public class OnStackTest {
public static class User{
public int id = 0;
public String name = "";
}
public static void alloc(){
User u = new User();
u.id = 5;
u.name = "jim";
}
public static void main(String[] args) {
long b = System.currentTimeMillis();
for(int i=0;i<100000000;i++){
alloc();
}
long e = System.currentTimeMillis();
System.out.println(e-b);
}
}
使用-Xmx10M -XX:+PrintGC 虚拟机参数运行代码:
[GC (Allocation Failure) 2048K->544K(9728K), 0.0015011 secs]
10
上述代码在主函数中进行了1亿次alloc()调用进行对象的创建,由于User对象实例需要占用约16byte的空间,因此累计分配空间将达到1.5G,如果堆空间小于这个值,就必然发生GC。而此时我们只分配了最大的堆内存为10M,如果这些对象在堆上创建,必然会引起大量的垃圾回收现象,查看垃圾回收日志,并没有。所以,说明其对象分配在栈上。
实例2:对比测试逃逸对象的分配空间位置:
package com.jvm;
public class OnStackTest {
public static class User{
public int id = 0;
public String name = "";
}
public static User u;
public static void alloc(){
u = new User();
u.id = 5;
u.name = "jim";
}
public static void main(String[] args) {
long b = System.currentTimeMillis();
for(int i=0;i<100000000;i++){
alloc();
}
long e = System.currentTimeMillis();
System.out.println(e-b);
}
}
同样使用虚拟机参数-Xmx10M -XX:+PrintGC设置最大堆空间和打印垃圾回收日志,运行此代码:

可见,发生大量的垃圾回收现象,说明此时堆内存远远不够,需要不断的进行垃圾回收。
4 方法区
和堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,比如类的字段、方法、常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区的溢出,虚拟机同样会抛出内存溢出错误。
在JDK1.6、JDK1.7中,方法区可以理解为永久区(Perm)。永久区可以使用参数-XX:PermSize和-XX:MaxPermSize指定,默认情况下,-XX:MaxPermSize为64M。一个大的永久区可以保存更多的类信息。如果系统使用了一些动态代理,那么有可能会在运行时生成大量的类,如果这样,就需要设置一个合理的永久区大小,确保不发生永久区内存溢出。
在JDK1.8中,永久区已经被彻底移除,取而代之的是元数据区,元数据区大小可以使用参数-XX:MaxMetaspaceSize指定(一个大的元数据区可以使系统支持更多的类),这是一块堆外的直接内存。与永久区不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。
如果元数据区发生异常,虚拟机一样会抛出异常。
JVM的基本结构及其各部分详解(二)的更多相关文章
- JVM的基本结构及其各部分详解(一)
1 java虚拟机的基本结构如图: 1)类加载子系统负责从文件系统或者网络中加载Class信息,加载的类信息存放于一块称为方法区的内存空间.除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字 ...
- jvm的基本结构以及各部分详解(转)
原文链接:https://www.cnblogs.com/zwbg/p/6194470.html 1.java虚拟机的基本结构 图: 1.类加载器子系统从文件系统或者网络中加载Class信息,类信息( ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- PopUpWindow使用详解(二)——进阶及答疑
相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...
- [转]文件IO详解(二)---文件描述符(fd)和inode号的关系
原文:https://www.cnblogs.com/frank-yxs/p/5925563.html 文件IO详解(二)---文件描述符(fd)和inode号的关系 ---------------- ...
- Linux dts 设备树详解(二) 动手编写设备树dts
Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...
- .NET DLL 保护措施详解(二)关于性能的测试
先说结果: 加了缓存的结果与C#原生代码差异不大了 我对三种方式进行了测试: 第一种,每次调用均动态编译 第二种,缓存编译好的对象 第三种,直接调用原生C#代码 .net dll保护系列 ------ ...
- Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
[Android布局学习系列] 1.Android 布局学习之——Layout(布局)详解一 2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数) 3.And ...
- logback -- 配置详解 -- 二 -- <appender>
附: logback.xml实例 logback -- 配置详解 -- 一 -- <configuration>及子节点 logback -- 配置详解 -- 二 -- <appen ...
随机推荐
- Spring中 @Autowired标签与 @Resource标签
spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解,如:@Resource. @PostConstruct及@PreDestroy. @Autowired ...
- 在pycharm中运行python程序
安装PyCharm 安装过程取决于您的操作系统: 在Windows上安装PyCharm 运行.exe您已下载的文件,并按照PyCharm安装向导的说明进行操作. 在macOS上安装PyCharm 打开 ...
- 如何进入docker 使用root用户的方式
docker exec -it --user root <container id> /bin/bash
- php格式化json字符串
header('content-type:application/json;charset=utf8'); $arr = array( 'status' => true, 'errMsg' =& ...
- linux——常用命令清单
目录 文件目录相关命令 压缩命令 关机重启命令 挂载命令 用户登录查看命令 搜索命令的命令 本文整理了学习过程中linux的一些常用操作命令,方便以后查阅.学习的课程是Linux达人养成计划 I. 文 ...
- 『TensorFlow』流程控制之tf.identity
一个详细介绍 下面程序要做的是,5次循环,每次循环给x加1,赋值给y,然后打印出来, x = tf.Variable(0.0) #返回一个op,表示给变量x加1的操作 x_plus_1 = tf.as ...
- 1、安装Angular-CLI脚手架工具
依赖环境的安装 1.安装node.js(版本在6以上) 查看版本号:node -v 2.安装npm(npm会随着node的安装一起被安装) 3.安装Python(我安装的是2.7.14),要安装环境变 ...
- easyui 如何为标签动态追加属性实现渲染效果
简述一下在项目遇到的问题,这边有一个需求,选择不同类型,加载不同的div标签(其中属性是否必填是区分类型的关键) html界面是这样的 <div class="grid_1 lbl&q ...
- JavaScript 设计模式的七大原则(未完成)
设计模式(面向对象)有七大设计原则,分别是: 开闭原则:对扩展开放,对修改关闭 单一职责原则:每一个类应该专注于做一件事情 里氏替换原则:父类存在的地方,子类是可以替换的 依赖倒转原则:实现尽量依赖抽 ...
- squid代理
概念 高性能dialing服务软件,作为前置缓存服务,用于替代用户向网站服务器请求页面数据并进行缓存. 默认占用端口3128.3401.4827 分类 从作用分类 ...