`

JVM学习笔记四 之 运行时数据区

    博客分类:
  • jvm
 
阅读更多

一、概述

运行时数据区是jvm运行时的内存布局,类装载到内存后存放的位置,为执行引擎提供所需指令和数据。运行时数据区包括:堆、栈、方法区、本地方法栈、pc计数器。

接下来会详细介绍各个部分,并介绍直接内存访问和方法区中的常量池,另外对于每个区域可能发生的内存异常用demo做讲解。

二、详细介绍各部分

1、堆

分配运行时产生的对象分配在堆中,但是并不是一定就分配在堆中,随着运行时编译器优化技术的不断进步,逃逸分析和标量替换技术的不断成熟,对象也可以分配到栈中。由于jvm内存是由虚拟机自己分配和回收,不需要开发人员手工干预,所以jvm一般会提供gc功能,但是jvm spec并没有明确要求必须提供。

整个系统只有一个堆,所有的对象分配都在一个逻辑区域。堆是一个多线程共享的区域,所以对堆中对象的访问就需要考虑并发。(jvm有什么措施)。

在jvm的实现中,一般堆都会划分成不同的区域,比如hotspot vm的堆划分为年轻代Eden、中生代Survivor和年老代Tenured区域,根据对象存活的年龄放到不同的区域,区域的大小可以通过参数设置。这些讲到gc的时候会详细讲解。

如果要存放的对象超过了堆的最大大小,则会抛出OutOfMemoryError,简称OOM异常。

2、栈

栈主要是运行时存放在方法调用信息(编译器已确定占用空间的东西怎么体现)的区域,栈是线程私有的,每个线程都有一个栈,线程生而栈生,线程死而栈灭。

栈中内存不需要额外收集,每次方法调用完成后都会自动回收。hospot vm中可以通过-Xss设置栈大小,默认是2m。

每次方法调用,都会产生一个栈帧,方法调用结束都会弹出栈帧。栈帧主要包括局部变量表、操作数栈、帧数据区。帧数据区又包括运行时解析常量池会用到的常量池指针、方法返回信息、指向异常的信息(还有什么)

所有栈占用的内存大小怎么计算?虚拟机整个的内存-堆内存-方法区内存-pc计数器-本地方法栈?

如果栈可以动态的在堆中分配,则如果分配不了更多的栈内存的时候会抛出OOM异常,否则会抛出StackOverflowError

3、方法区

方法区存class文件加载到内存后的类型信息,如类型描述符、全限定名、接口、字段、方法、属性信息,还有运行时常量池、指向堆中Class实例和ClassLoader实例的信息。另外还存放jit编译产生的本地代码。

方法区在逻辑上是堆的一部分,但是不同的虚拟机有不同的叫法,hotspot叫做non-heap内存,IBM J9和Jrockit则没有这种叫法。hotspot后续版本也会去掉这种叫法,放到native memory的区域

方法区的类型信息也可以卸载,及gc,但是比较严格,像java动态代理生成的代理类经常会被卸载。类型信息要被卸载必须满足这些条件:a、类型所有实例都被回收了 b、Class对象被回收了 c、加载该类的ClassLoader已经被回收 d、还有什么

该区域也会内存溢出,溢出时候抛出OOM异常,如果jar包太多而方法区内存太小或者运行时动态向常量池添加太多东西,就会抛出OOM

4、本地方法栈

执行本地代码的时候用到的栈,hotspot的本地方法栈和栈是同一块区域。

5、pc计数器

指向当前线程将要指向的下一条指令,线程私有的区域,一个线程有一个pc计数器。该区域不会抛出异常。一个字长

6、Direct Memory

java1.4开始提供了NIO,大量读取数据的时候,不用先把数据拷入jvm内存中,直接从os内存读取。所以该区域不占用jvm的堆内存,通过参数-XX:MaxDirectMemorySize设置该区域大小,如果不设置默认跟堆大小一样大。

该区域会抛出OOM异常,何时?

7、常量池

类文件的常量池装载进内存后同样会形成常量池,这是运行时常量池。与类文件常量池不同的是,运行时常量池在使用的时候需要解析,需要把一些entry的符号引用转化成直接引用。

如3提到,此区域在内存不够用时会抛出OOM。

三、例子

1、堆

先创建一个包含大对象的类

package com.yymt.jvm;
public class BigObejct {
	int[] value;
	private static final int M1 = 1024 * 1024;

	public BigObejct() {
		//4 * 1m = 4m
		this.value = new int[M1];
	}
}

    循环创建类,注释中-Xmx10m是启动时虚拟机参数,最大堆内存是10m

package com.yymt.jvm.oom;

import com.yymt.jvm.BigObejct;

public class HeapOOM {

	/**
	 * -Xmx10m
	 * @param args
	 */
	public static void main(String[] args) {
		BigObejct[] objs = new BigObejct[20];
		for(int i = 0;i < 20;i++){
			objs[i] = new BigObejct();
		}
	}

}

堆溢出,注意此处OutOfMemoryError: Java heap space表明是堆空间溢出

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.yymt.jvm.BigObejct.<init>(BigObejct.java:8)
	at com.yymt.jvm.oom.HeapOOM.main(HeapOOM.java:15) 

   2、栈溢出错误StackOverflowError

package com.yymt.jvm.oom;

public class StackOverflow {

	/**
	 * -Xss500k 如果加上此参数则不会出现StackOverflowError
	 * @param args
	 */
	public static void main(String[] args) {
		recursiveInvoke(5000, 1, 1);
	}
	public static void recursiveInvoke(int cnt,int param1,int param2){
		if(cnt == 0){
			return;
		}
		int newCnt = cnt - 1;
		int param3 = param1 + param2;
		int param4 = param1 - param2;
		recursiveInvoke(newCnt, param4, param3);
	}

}

 不加-Xss参数时候就直接异常了,至少在我的机子上如此;-Xss100k也可以出异常,-Xss500k就正常运行

Exception in thread "main" java.lang.StackOverflowError
	at com.yymt.jvm.oom.StackOverflow.recursiveInvoke(StackOverflow.java:13)
	at com.yymt.jvm.oom.StackOverflow.recursiveInvoke(StackOverflow.java:19)
......

  3、方法区

3.1、如果系统非常庞大,比如说一个很大的产品,如ERP系统、CRM系统,里边有非常多的jar包,运行期这些jar都要加载到内存中,而如果方法区设置的过小,就会导致OOM。在客户分阶段上线过程中,第一阶段只上部分模块,比如erp系统只上财务模块,第二期上供应链,第三期上hr、制造,如果前期MaxPermSize设置的过小,就会导致后期部署完相关模块运行过程中出现方法区溢出。此处因为系统代码庞大,代码无法上传。注意此处是PermGen space

java.lang.OutOfMemoryError: PermGen space

  3.2、如果动态创建无数的类放到内存中同样会出现这个问题

package com.yymt.jvm.oom;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class PermGenOOM {

	/**
	 * -XX:MaxPermSize=10m
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		for (int i = 0; i < 5000000; i++) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(DynClz.class);
			enhancer.setUseCache(false);
			enhancer.setCallback(new MethodInterceptor() {
				@Override
				public Object intercept(Object arg0, Method arg1,
						Object[] arg2, MethodProxy arg3) throws Throwable {
					return arg3.invokeSuper(arg0, arg2);
				}
			});
			enhancer.create();
			Thread.sleep(20);
		}
	}
}

 

 异常:

 

Caused by: java.lang.OutOfMemoryError: PermGen space

  4、Direct Memory

package com.yymt.jvm.oom;
import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class DirectMemoryOOM {

	private static final int M1 = 1024 * 1024;

	/**
	 * @param args
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws IllegalArgumentException,
			IllegalAccessException, InterruptedException {

		Field unsafeFld = Unsafe.class.getDeclaredFields()[0];
		unsafeFld.setAccessible(true);
		Unsafe unsafe = (Unsafe) unsafeFld.get(null);
		for (int i = 0; i < 1000000; i++) {
			unsafe.allocateMemory(M1);
			Thread.sleep(2);
		}
		Thread.sleep(100000);

	}

}

这部分内存的使用情况,目前visualvm工具中是检测不到的

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at com.yymt.jvm.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:24)
 

 

分享到:
评论
1 楼 飞猪传说 2011-11-02  
小样真牛逼啊!!

相关推荐

    JVM学习笔记核心知识点整理

    JVM学习笔记核心知识点整理,包含类文件加载机制,运行时数据,JVM内存模型,GC算法,垃圾收集器分类等

    JVM 虚拟机.md

    JVM 学习,包含运行数据区域,GC,JMM 等常见问题

    JVM学习笔记(一)——类的加载机制

    ​ 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 ...

    运行时数据区概述及线程.xmind

    自己总结的jvm中运行时数据区概述及线程的笔记,绘制了详细的思维导图,每个思维导图中均有详细的博文解释,方便大家学习和理解,免费分享给大家。适合jvm的爱好者和学习者

    笔记 — JVM内存结构

    Java虚拟机(Java Virtual Machine,简称JVM),Java的“一处编译,处处运行”,就是因为Java程序编译成字节码文件后可以在任何计算机的JVM上执行,所以JVM是我们学习Java的重点之一。 JVM = 类加载器(classloader) + ...

    Java基础学习笔记(印象笔记)

    Java基础学习笔记(印象笔记) Java 是一种广泛使用的面向对象编程语言,最初由 Sun Microsystems(现为 Oracle Corporation)于1995年发布。它具有简单、易学、可移植、安全和高性能等特点。 Java 是一种跨平台语言...

    Java 基础学习笔记:数据类型,常见运算,final &amp;amp; static,Java 常见类,异常 &amp;amp; 反射

    Java 基础学习笔记,主要包括: 10_Java常见对象.md 10_Java常见对象_2.md 1_数据类型.md 3_运算.md 4_Object通用方法.md 5_关键字.md 6_反射.md 8_泛型.md JDK8新特性.md 正则表达式.md Java是一种面向对象的编程...

    leetcode题库-Blog:Fashion'sBlog个人学习笔记,涵盖JVM、数据结构、算法、设计模式、中间件、数据库、缓存、分布式微服

    运行时数据区 垃圾回收 类加载过程 类加载器 双亲委派 JVM性能调优监控工具 设计模式 消息中间件 RocketMQ 概念 RocketMQ 特性 RocketMQ 集群 RocketMQ 事务 RocketMQ 常见问题 数据库 Redis 基本数据结构 进阶使用 ...

    net学习笔记及其他代码应用

    答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 40.接口是否可...

    Java学习笔记

    在不同的操作系统之上可以不用做任何代码的修 改 直接使用 a) 字节码文件:字节码文件不包括任何内存布 局信息 与操作系统和硬件毫无关系 (Java 的内存分布是在运行的时候才动态分配的) b) JVM:真正解释字节码文件...

    关于学习笔记整理,包括Linux、Java、数据结构和算法,和一些IT工具的使用.zip

    它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)...

    毕设&课设&项目&实训-【java开发笔记指北】涵盖java、JVM、Spring、常用框架.zip

    毕设&课设&项目&实训-【java开发笔记指北】涵盖java、JVM、Spring、常用框架、中间件、数据库、数据结构与算法、设计模式 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、...

    java8rt.jar源码-JVM:学习JVM

    JVM的重要性不言而喻,这个是学习JVM是看视频和读《深入理解JVM》时做的一些笔记,用于复习参考。 读书笔记 第2章:java内存模型和内存溢出异常 1.运行时数据区域 1.程序计数器:线程私有 2.java虚拟机栈:线程私有...

    Java并发编程(学习笔记).xmind

    这是一种两方栅栏,各方在栅栏位置上交换数据。 应用场景:当两方执行不对称的操作(读和取) 线程池 任务与执行策略之间的隐形耦合 线程饥饿死锁 运行时间较长的任务 设置线程池的大小 ...

    springboot学习思维笔记.xmind

    springboot学习笔记 spring基础 Spring概述 Spring的简史 xml配置 注解配置 java配置 Spring概述 Spring的模块 核心容器CoreContainer Spring-Core Spring-Beans ...

    java简易版开心农场源码-JavaOfferLessonStudy:剑指Java_Offer直通车学习笔记

    ​ 控制子网的运行,如逻辑编制、分组传输、路由选择 1.1.4 传输层(TCP、UDP) ​ 接受上一层的数据,在必要的时候把数据进行分割,并将这些数据交给网络层,且保证这些数据段有效到达对端 1.1.5 会话层 ​ 不同...

    notebook:喜欢的,值得留念的,就记下来,总会有用的

    运行时数据区域 垃圾收集机制 深入理解JAVA并发与集合 前言 并发与集合的定义 JAVA语言的水深 如何学习并发与集合 项目成果 qlogfetch2 文章目录 第0章:源码分析 第1章:JAVA线程基础 第2章:并发三大特性 第3章:...

    Python的Spark:Python的Spark基础(使用PySpark),代码示例

    它运行速度快(由于在内存中进行操作,因此比传统的快100倍,提供健壮的,分布式的,容错的数据对象(称为 ),并通过诸如的补充包与机器学习和图形分析领域完美集成和 。 Spark在上实现,并且主要用 (一种类似于...

Global site tag (gtag.js) - Google Analytics