Java虚拟机内存模型

1)程序计数器:线程私有

当线程数量超过CPU数量时,线程之间根据时间片轮询抢夺CPU资源,对于单核CPU来说,每一个时刻,只能有一个线程在运行,而其他线程必须被切换出去。因此,每个线程都有一个独立的程序计数器,用于记录下一条要运行的指令,各个线程之间的计数器互不影响,是一块线程的私有内存空间。当一个线程正在执行一个Java方法时,程序计数器记录正在执行的Java字节码地址,如果执行的是native方法,则计数器为空
2)Java虚拟机栈:线程私有

同Java线程同时间创建,用于保存方法的局部变量,部分结果,并参与方法的调用和返回。Java虚拟机定义了两种与栈空间有关的异常:StackOverflowError,OutOfMemoryError。当请求的栈深度大于最大可用的栈深度,抛出StackOverflowError。如果栈可以动态扩展,扩展栈的过程中,没有足够的内存空间来支持栈的扩展(系统内存不足?),则抛出OutOfMemoryError。以下为一个抛出java.lang.StackOverflowError的实例

public class TestStack{
    private static int count=0;
    public static void recursion(long a,long b,long c){
        long d=0;
        long e=0;
        long f=0;
        count++;
        recursion(a,b,c);
    }
    public static void main(String []args){
        try{
            recursion(1L,2L,3L);
        }catch(Throwable e){
            System.out.println("count:"+count);
            System.out.print(e);
        }
    }
    public void recursion1(long a,long b,long c){
        long d=0;
        long e=0;
        long f=0;
        count++;
        recursion1(a,b,c);
    }
}
/*
直接运行
输出:
    count:4927
使用-Xss来扩大栈空间,运行 java -Xss1m TestStack
输出:
    count:27590
*/

虚拟机栈在运行时使用了一种叫做栈帧的数据结构保存上下文数据,在栈帧中,存放了方法的局部变量表、操作数栈、动态链接方法和返回地址等信息。每个方法的调用都伴随着栈帧的入栈操作,方法的返回则表示栈帧的出栈操作。方法调用时,方法的参数和局部变量越多,局部变量表就越大,栈帧相应变大以满足方法调用所需传递的信息。因此,单个方法调用所需的栈空间大小也会比较多

结论:使用-Xss来扩大栈空间,增加栈空间大小后,函数调用深度上升。函数的局部变量越多,栈帧就越大,单次函数调用对栈空间的需求也会增加,函数嵌套调用次数减少

局部变量表的空间是可重用的,对于一个方法所分配的最大局部变量表的容量,可以使用jclasslib工具来查看

public class Test1{

    public static void main(String []args){
        test3();
    }
    public static void test1(){
        {
            byte[] b=new byte[6*1024*1024];
        }
        //此时b仍然在该栈帧的变量表中,
        //GC根可以引用到该内存块,又未能有足够多的局部变量来服用该内存块,
        //因此不会被回收[1]
        System.gc();
        System.out.println("first explict gc over");
    }
    public static void test2(){
        {
            byte[] b=new byte[6*1024*1024];
            b=null;//帮助系统GC
        }
        //被回收[2]
        System.gc();
        System.out.println("first explict gc over");
    }
    public static void test3(){
        {
            byte[] b=new byte[6*1024*1024];
        }
        int a=0;//服用b的内存块,GC根无法找到b,因此被回收[3]
        System.gc();
        System.out.println("first explict gc over");
    }
}
/*
java -verbose:gc Test1
--[1]
[GC 224K->134K(5056K), 0.0027563 secs]
[Full GC 134K->134K(5056K), 0.0177525 secs]
[Full GC 6278K->6278K(11204K), 0.0151799 secs]
first explict gc over

--[2]
[GC 224K->134K(5056K), 0.0027134 secs]
[Full GC 134K->134K(5056K), 0.0178466 secs]
[Full GC 6278K->134K(11204K), 0.0152811 secs]
first explict gc over

--[3]
[GC 224K->134K(5056K), 0.0027211 secs]
[Full GC 134K->134K(5056K), 0.0174787 secs]
[Full GC 6278K->134K(11204K), 0.0147169 secs]
first explict gc over
*/

3)本地方法栈:
与Java虚拟机栈功能相似,Java虚拟机栈用于管理Java函数调用,而本地方法栈用于管理本地方法的调用,有C实现。在Hot Spot虚拟机中不区分本地方法栈和Java虚拟机栈,因此,同样也会抛出StackOverflowError,OutOfMemoryError异常

4)Java堆:Java运行时内存中最重要的部分
分配运行时所有的对象和数组。分为新生代(eden,survivor space0[s0,from space],survivor space1[s1,to space])和老年代。

5)方法区:线程共享
方法区存放的是类的类型信息,常量池,域信息,方法信息等大部分来自class文件的信息。在Hot Spot虚拟机中,方法区也叫做永久区,是一块独立于Java堆的内存空间,同样也可以被GC回收,只是表现和Java堆不同

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
public class PermGenGC{
    public static void main(String []args){
        List<String> list=new ArrayList<String>();
        int i;
        for(i=0;i<Integer.MAX_VALUE;i++){
            //如果常量池中存在当前String,则返回池中对象,如果不存在,则先将String加入到常量池,再返回池中对象引用
            list.add(String.valueOf(i).intern());//加入到常量池
        }
    }
}
/*
java -XX:PermSize=2m -XX:MaxPermSize=4m PermGenGC

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at PermGenGC.main(PermGenGC.java:7)

java -XX:PermSize=2m -XX:MaxPermSize=4m -XX:+PrintGCDetails PermGenGC

[GC [DefNew: 896K->64K(960K), 0.0043064 secs] 896K->180K(5056K), 0.0050202 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [DefNew: 919K->0K(960K), 0.0031302 secs] 1035K->236K(5056K), 0.0039759 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC [DefNew: 778K->0K(960K), 0.0029668 secs] 1015K->390K(5056K), 0.0036493 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 896K->0K(960K), 0.0030697 secs] 1286K->619K(5056K), 0.0037499 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 896K->0K(960K), 0.0033067 secs] 1515K->964K(5056K), 0.0040117 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC [Tenured: 964K->581K(4096K), 0.0595687 secs] 1665K->581K(5056K),
[Perm: 4095K->4095K(4096K)], 0.0610942 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
[Full GC [Tenured: 581K->581K(4096K), 0.0591349 secs] 581K->581K(5056K),
[Perm : 4095K->4095K(4096K)], 0.0636965 secs] [Times: user=0.06 sys=0.01, real=0.06 secs]
Exception in thread "main"
[Full GC [Tenured: 581K->236K(4096K), 0.0289138 secs] 596K->236K(5056K),
[Perm : 4095K->360K(4096K)], 0.0332241 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at PermGenGC.main(PermGenGC.java:8)
*/
时间: 2024-10-17 02:01:36

Java虚拟机内存模型的相关文章

Java虚拟机内存模型及垃圾回收监控调优

Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存模型         正如你上图所看到的,JVM内存可以划分为不同的部分,广义上,JVM堆内存可以划分为两部分:年轻代和老年代(Young Generation and Old Generation) 年轻代(Young Generation) 年轻代用于存放由new所生成的对象.当年轻代空间满时,

Java虚拟机内存模型和volatile型变量

Java虚拟机内存模型 了解Java虚拟机的内存模型,有助于我们明白为什么会发生线程安全问题. 上面这幅图是<深入理解Java虚拟机-JVM高级特性与最佳实践>的书中截图. 线程共享的变量会保存在主内存中(Main Memory). 而线程共享的变量的副本会保存在每个线程各自的工作内存中(Working Memory). 线程对于共享变量的所有操作(读取,赋值等)都必须在工作内存中进行,不能直接读写主内存的变量. 不同的线程之间,也无法访问其他线程的工作内存.线程之间的变量传递需要通过主内存来

【转】JVM虚拟机内存模型

转发声明: 本文原创作者:书呆子Rico 作者博客地址:http://blog.csdn.net/justloveyou_/ 摘要: 我们都知道,Java程序在执行前首先会被编译成字节码文件,然后再由Java虚拟机执行这些字节码文件从而使得Java程序得以执行.事实上,在程序执行过程中,内存的使用和管理一直是值得关注的问题.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域都有各自的用途,以及创建和销毁的时间,并且它们可以分为两种类型:线程共享的方

java中JVM虚拟机内存模型详细说明

java中JVM虚拟机内存模型详细说明 2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅 JVM的内部结构如下图: 一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能. 本文将从JVM内存模型.GC工作原理,以及GC的几个关键问题进行探讨,从

Java虚拟机内存分配详解

简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数据结构,这种方式的特点,让程序员编程时,必须特别注意递归方法要尽量少使用,另外栈的大小也有一定的限制,如果过多 的递归,容易导致stack overflow. 2.了解Java内存管理的细节,一旦内存管理出现问题,有助于找到问题的根本原因所在. 3.了解Java内存管理的内幕,有助于优化JVM,从而

Java虚拟机内存管理机制

自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动和结束而建立和销毁.根据<Java虚拟机规范 第2版>规定,运行时数据区包括: 1.程序计数器 一块较小的内存空间,不在Ram上,而是直接划分在CPU上的,程序员无法直接操作它.当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令.每条

Java虚拟机内存区域详解

众所周知,Java程序运行于Java虚拟机(JVM)上,那么,JVM运行的时候内存是如何分配的呢?程序中各部分变量都存储在内存的哪个部分,又如何访问,下面,就让我来给大家讲解Java虚拟机内存区域. 为什么需要了解Java虚拟机内存区域 相对于C++程序员,因为虚拟机的自动内存管理机制的存在,Java程序员很多时候并不需要去担心内存的泄露和内存溢出的问题.但是正是因为把内存的控制权交给了JVM,一旦出现内存泄露和溢出的问题,如果不了解虚拟机怎么使用内存,就很难排查错误. 内存泄露:一些对象在使用

java线程内存模型,线程、工作内存、主内存

转自:http://rainyear.iteye.com/blog/1734311 java线程内存模型 线程.工作内存.主内存三者之间的交互关系图: key edeas 所有线程共享主内存 每个线程有自己的工作内存 refreshing local memory to/from main memory must  comply to JMM rules 产生线程安全的原因 线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计算机,cpu在计算的时候,并不总是从内存读

java 虚拟机内存介绍

已经入行快有两年了,然而没有认真看过java虚拟机内存的介绍,下面把自己看的关于java虚拟机内存的介绍写下来,分享给大家. java 虚拟机在运行时将内存分为几个区域,分别为程序记数区,java虚拟机栈,本地方法栈,java堆,方法区,运行常量池.此外还有一个叫做直接内存的区域,虽然不是java虚拟机的一部分,但是也非常重要. 程序计数器 顾名思义就是记录当前线程执行的字节码的行号,由于java虚拟机中的多线程是通过多线程轮流切换来实现多线程的,为了线程在切换后能回复到正确的执行位置,每个线程