对于Java程序员来说,内存是由JVM自动管理的,所以一旦出现内存泄漏或溢出的问题,不了解JVM的内存结构和各个内存区域的工作职责,将对解决问题带来很大的麻烦,本文参照周志明的《深入理解Java虚拟机》,介绍JVM的内存结构,比较枯燥,但对知其然,不知所以然的编码人员来说还是有一定帮助的。

  按照Java虚拟机规范的规定,JVM自动管理的内存将会包括以下几个运行时数据区域。

   

  程序计数器

  程序计数器(Program Counter Register)是JVM中一块较小的内存区域,保存着当前线程执行的虚拟机字节码指令的内存地址。Java多线程的实现,其实是通过线程间的轮流切换并分配处理器执行时间的方式来实现的,在任何时刻,处理器都只会执行一个线程中的指令。在多线程场景下,为了保证线程切换回来后,还能恢复到原先状态,找到原先执行的指令,所以每个线程都会设立一个程序计数器,并且各个线程之间不会互相影响,程序计数器为"线程私有"的内存区域。

  如果当前线程正在执行Java方法,则程序计数器保存的是虚拟机字节码的内存地址,如果正在执行的是Native方法(非Java方法,JVM底层有许多非Java编写的函数实现),计数器则为空。程序计数器是唯一一个在Java规范中没有规定任何OutOfMemory场景的区域。

  虚拟机栈

  虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈,所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程。

  虚拟机栈是一个后入先出的数据结构,线程运行过程中,只有一个栈帧是处于活跃状态的,被称为"当前活动帧栈",当前活动帧栈始终是虚拟机栈的栈顶元素。

  

  本地方法栈

  本地方法栈(Native Method Stack)和虚拟机栈的作用相似,不过虚拟机栈是为Java方法服务的,而本地方法栈是为Native方法服务的。

  方法区

  方法区(Method Area)是用于存储类结构信息的地方,包括常量池、静态变量、构造函数等类型信息,类型信息是由类加载器在类加载时从类文件中提取出来的。

  方法区同样存在垃圾收集,因为用户通过自定义加载器加载的一些类同样会成为垃圾,JVM会回收一个未被引用类所占的空间,以使方法区的空间达到最小。

  方法区中还存在着常量池,常量池包含着一些常量和符号引用(加载类的连接阶段中的解析过程会将符号引用转换为直接引用)。

  方法区是线程共享的。

  

  堆(heap)是存储java实例或者对象的地方,是GC的主要区域,同样是线程共享的内存区域。

  案例分析一

  

  案例分析二

  

  上面main方法中运行的程序过程如下:

  (1)用户创建了一个Student对象,运行时JVM首先会去方法区寻找该对象的类型信息,没有则使用类加载器classloader将Student.class字节码文件加载至内存中的方法区,并将Student类的类型信息存放至方法区。

  (2)接着JVM在堆中为新的Student实例分配内存空间,这个实例持有着指向方法区的Student类型信息的引用,引用指的是类型信息在方法区中的内存地址。

  (3)在此运行的JVM进程中,会首先起一个线程跑该用户程序,而创建线程的同时也创建了一个虚拟机栈,虚拟机栈用来跟踪线程运行中的一系列方法调用的过程,每调用一个方法就会创建并往栈中压入一个栈帧,栈帧用来存储方法的参数,局部变量和运算过程的临时数据。上面程序中的stu是对Student的引用,就存放于栈中,并持有指向堆中Student实例的内存地址。

  (4)JVM根据stu引用持有的堆中对象的内存地址,定位到堆中的Student实例,由于堆中实例持有指向方法区的Student类型信息的引用,从而获得add()方法的字节码信息,接着执行add()方法包含的指令。

   总结

   1、所有线程共享的内存数据区:方法区,堆。而虚拟机栈,本地方法栈和程序计数器都是线程私有的。

   2、存放于栈中的东西如下:
      2.1 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)。对象都存放在堆区中。

    2.2 每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
      2.3 方法的形式参数,方法调用完后从栈空间回收
      2.4 引用对象的地址,引用完后,栈空间地址立即被回收,堆空间等待GC

   3、存放于堆中的东西如下:

    3.1 存储的全部是对象,每个对象包含一个与之对应的class信息

    3.2 Jvm只有一个堆区(heap)被所有线程共享,堆区中不存放基本类型和对象引用,只存放对象本身

   4、存放于方法区中的东西如下:

    4.1 存放线程所执行的字节码指令

    4.2 跟堆一样.被所有线程共享.方法区包含:所有的class和static变量

    4.3 常量池位于方法区中,见如下图示说明

    

JVM内存结构分析的更多相关文章

  1. Java基础知识强化100:JVM 内存模型

    一. JVM内存模型总体架构图:  方法区和堆由所有线程共享,其他区域都是线程私有的 二. JVM内存模型的结构分析: 1. 类装载器(classLoader) 类装载器,它是在java虚拟机中用途是 ...

  2. JVM内存管理学习总结(一)

    I.JVM进程的生命周期 JVM实例的生命周期和java程序的生命周期保持一致,即一个新的程序启动则产生一个新的JVM进程实例,程序结束则JVM进程实例伴随着消失.那么程序启动和程序终止就是JVM实例 ...

  3. Inside JVM 内存模型

    Inside JVM 内存模型 来源  原文:https://blog.csdn.net/silentbalanceyh/article/details/4661230 参考:IBM开发中心文档,&l ...

  4. JVM内存模型 三

    本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏   1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉 ...

  5. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

  6. jvm系列(二):JVM内存结构

    JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...

  7. jvm内存溢出分析

    概述 jvm中除了程序计数器,其他的区域都有可能会发生内存溢出 内存溢出是什么? 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出 内存溢出和 ...

  8. jvm内存区域

    概述 jvm内存分为几个区域: 程序计数器 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 直接内存 这些内存区域是在java进程中细分的,为java程序提供服务 不同的区域存储的内容不一样,生命周期 ...

  9. JVM内存管理&GC

    一.JVM内存划分 |--------------------|-------------PC寄存器-------| |----方法区 ---------|--------------java 虚拟机 ...

随机推荐

  1. Java开发工程师面试题1

    时间2019-1-2 地点上海 package service; import java.text.SimpleDateFormat; import java.util.Date; public cl ...

  2. [laravel]malformed header from script 'index.php': Bad header: HTTP/1.1 302 Found, referer: http://localhost/auth/login

    修改php.ini中的 cgi.rfc2616_headers = 0 cgi.force_redirect = 1

  3. Oracle 行列转置

    两种简单的行列转置 1.固定列数的行列转换如student   subject    grade--------- ---------- --------student1  语文       80st ...

  4. M - COURSES

    Consider a group of N students and P courses. Each student visits zero, one or more than one courses ...

  5. 使用redis接管cookie

    class RedisCookie { // 默认配置名称(使用load_config加载) private $_default_config_path = 'package/cache/redis_ ...

  6. D5 F

    最近见到了好多跟排列有关的状压dp,好像略微会了一点,用 dp[i][s][j]表示第i位状态为s选择j的方案数,然后递推. 早起大概可以提高人的智商但是会导致人甚至不清,初始化写错了自闭了半个小时 ...

  7. node 把文件封装一层文件夹

    把 pages 下面的单个js文件,封装上一个文件夹 var glob = require("glob"); const fs = require("fs-extra&q ...

  8. ASP.NET MVC中使用FluentValidation验证实体(转载)

    1.FluentValidation介绍 FluentValidation是与ASP.NET DataAnnotataion Attribute验证实体不同的数据验证组件,提供了将实体与验证分离开来的 ...

  9. arcpy加载mxd文件时,无效的MXD路径,提示assert (os.path.isfile(mxd) or (mxd.lower() == "current")), gp.getIDMessage(89004, "Invalid MXD filename")

    无效的MXD路径,将路径前加‘u’,改为这种: mxdPath = u"C:\\1331\\DB\\Original Files\\dd.mxd" 参考: https://gis. ...

  10. Linux下/usr/bin与/usr/local/bin/区别总结

    Linux下/usr/bin与/usr/local/bin/区别总结 2017年10月13日 12:30:17 2puT 阅读数:15930    版权声明:本文为博主原创文章! github地址:h ...