`

Jvm的内存结构

    博客分类:
  • jvm
阅读更多

前言

 

Jvm的内存结构是由《java虚拟机规范》制定的,《java虚拟机规范》只负责制定标准,具体的实现多种多样,比如:sun公司的HotSpotBEAJRockitIBMJ9(前两个目前都已被Oracle收购),另外ApacheGoogle、微软等组织或公司都有自己的java虚拟机实现。只是我们目前开发比较常用的是HotSpot

 

java虚拟机规范》与具体的java虚拟机的关系,有点类似于接口和实现类。《java虚拟机规范》负责制定接口,具体的java虚拟机去实现这些接口。我们编写的java代码最终会被编译成class文件,通过java虚拟机解析加载执行。Java语句的“跨平台性”,其实就是通过java虚拟机实现的,不同的平台需要实现不同jvm版本(windows版、linux版等)。我们业务代码只需要开发一份,编译成同一份class文件,却可以在windowslinux等不同的操作系统中执行。

 

这里提到的class文件的格式、规范也是由《java虚拟机规范》制定的。换句话讲不管采用什么语言编写的程序,只要按照《java虚拟机规范》编译成class文件就可以在java虚拟机中执行。比如现在常见的可以在jvm(等同java虚拟机)上运行的语言:ScalaGroovyJython等等数十种语言(笔者只使用过这里列出三种)。这是jvm虚拟机除了“跨平台性”之外的另一个强大之处—“跨语言性

 

现在越来越多的语言都有基于jvm实现版本,比如:JavaScript对应的RhinoLua对应的LuajPython对应的Jyhon等等。利用jvm跨语言特性,可以实现不管你使用什么语言编写的代码最终可以在同一个平台jvm中运行,实现跨语言调用。Jvm不再是java语言的专属,它属于世界上个各种编程语言。

 

为什么这么多语言都要争相实现基于jvm的实现版本呢?前面已经提到两点:借助jvm可以实现跨平台;借助jvm可以实现跨语言。还有一点其实跟今天主题相关:借助jvm实现自动“内存管理”。

 

众所周知,与cc++不同(需要自己控制内存),java可以自动实现垃圾内存回收。其实这份工作不是java语言本身实现的,而是jvm实现的,也就是说任何一种其他语言只要能遵循《java虚拟机规范》可以编译成可以执行class文件,其内存管理就可以放心的交给jvm

 

话又说回来,主流的jvm实现本质上还是使用的cc++写的(当然理论上用什么语言写都可以,只要符合《java虚拟机规范》),其内存管理还是通过cc++控制内存空间的开辟和销毁。只是把这部分工作交给了jvm来做,java程序员只需要关心自己的业务逻辑即可。这有点类似架构设计,架构师把通用的功能提到框架中统一处理,普通程序员只需编写业务代码即可。或者可以说Jvm是更顶层的架构设计,只是架构师变成jvm源码实现那帮家伙而已。

 

另外《java虚拟机规范》有多个版本,笔者只阅读过周老师翻译的《java虚拟机规范 java SE7》,感兴趣的可以直接研究oracle官网最新的版本(英文原版)。

 

Jvm的内存结构

 

根据java虚拟机规范可以完成java虚拟机的开发,Java虚拟机可以看作是一台抽象的计算机。如同真实的计算机那样,它有自己的指令集以及各种运行时内存区域。本次主题是讨论jvm的内存结构,对应的就是jvm“运行时内存区

 

该区域大致分为5部分:方法区、java堆、PC寄存器(或者程序计数器)java虚拟机栈、本地方法栈。大致流程为程序启动时:先把class文件加载到方法区;初始化bean对象放到java堆(比如spring ioc容器中的bean);每个线程执行时会对应一个自己私有的PC寄存器;同时还会创建一个私有的 java虚拟机栈”或者本地方法栈

 

由此可以看出:方法区和java堆是所有线程公有的,PC寄存器、java虚拟机栈和本地方法栈是线程私有的。结构图如下(来至《深入理解Java虚拟机):

 


 

 

1、方法区

 

在程序启动时jvm会读取class文件,把每个类的结构信息放到方法区,这里的class文件包括jar包、war包中的所有class文件。所以程序中应该尽量避免引入无用的、重复的(不同版本)jar包。类的结构信息包括:运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法(<init><cinit>方法)。该区域所有线程共享。

 

jdk1.8(对应不同的jvm实现)以前,我们常用的hotspot java虚拟机内存分代中有永久代的概念,这个永久代等价于“方法区”。可以通过PermSize MaxPermSize来设置永久代大小。如果超过MaxPermSize,系统会抛出OutOfMemoryError: PermGen异常。

 

jdk1.8之后,hotspot jvm在内存空间中完全移除了永久代。“方法区”中“类的元数据信息”被放到“元空间”(Metaspace),运行时常量池被放到“java堆”(这部分是从jdk1.7开始)。

 

jdk1.8之后,PermSize MaxPermSize参数设置失效,启动时会有警告信息。改用新的参数MaxMetaspaceSize来限制“元空间”的大小,如果不设置默认最大为本机内存容量(动态调整)。建议通过MaxMetaspaceSize设置最大“元空间”,如果类元数据的空间占用达到参数“MaxMetaspaceSize”设置的值,将会触发对死亡对象和类加载器的垃圾回收。

 

2java

 

通过-Xms -Xmx指定堆内存大小,Java堆在jvm启动时创建,所有对象的创建和销毁都在这个区域进行,是jvm管理的最大的一块内存(可以不是连续的)。对象的销毁指的就是垃圾回收,为了更合理的回收对象(对象存活时间的长短),通常的jvm实现把java堆分为年轻代和年老代,并采用不同的垃圾回收算法、以及垃圾回收器进行垃圾回收(对象销毁)。这里不对分代算法、垃圾回收器详细讲解。

 

该区域中的对象是所有线程共享的,典型的运用场景就是springioc容器,在程序启动时创建一系列对象放到一个全局的map数据结构里,防止被垃圾回收。线程中可以直接使用这些对象,而不需要重复创建和销毁。

 

在创建新对象时,如果该区域已没有足够的空间 会抛出OutOfMemoryError异常

 

3程序计数器

 

由于jvm是支持多线程执行的,但本质上是cpu在多个线程之间切换,为了记录每个线程执行的位置,每个线程都有自己独有的程序计数器,多个线程之间互不干扰。对于非native方法程序计数器记录的是正在执行的虚拟机字节码指令地址,可以看做所执行字节码的指示器,通过字节解释器改变其值来保证程序按照特定的顺序执行。

 

PC寄存器的容量至少应当能保存一个returnAddress类型的数据或者一个与平台相关的本地指针的值,其大小是能确定的。

 

该区域需要注意以下三点:

1.如果线程正在执行的是非native方法,那么计数器记录的是正在执行的虚拟机字节码指令地址

2.如果执行的native方法,计数器当中的内容应当是空。执行顺序交给jvmnative方法控制。

3.该区域是java的虚拟机规范当中,唯一一个没有规定OutOfMemoryError的区域。

 

4java虚拟机栈

 

每一条java虚拟机线程在创建时会创建自己的java虚拟机栈,因此它是线程私有的。其主要作用是:用于存储局部变量与一些过程计算结果的地方。其工作方式是结合程序计数器读入变量到栈,根据不同的指令读取值出栈进行运算(先入后出),运算结果再入栈。

 

java虚拟机栈的总容量可以动态扩展,但每个线程的栈大小是固定的,可以通过-Xss参数指定。总内存固定 其值越小就可以支持创建更多的线程,同时每个栈的容量就越小,当该线程需要的容量超过这个值时,就会抛出StackOverflowError异常。同理 如果-Xss参数指定的值越大,每个线程可以用的栈内存空间就越大可以存放更多的局部变量等信息,但支持的线程就越小,如果总内存耗尽 没有足够的空间开辟新的线程,会抛出OutOfMemoryError异常。简单的讲如果抛出抛出StackOverflowError异常,增大-Xss的值;如果抛出OutOfMemoryError异常,减小-Xss的值。

 

Java虚拟机栈里数据结构叫栈帧

栈帧随着方法调用而创建,随着方法结束而销毁,也就是说一个线程里执行多个方法,对应会产生和销毁多个栈帧,这里的销毁时机是指方法调用结束,也就是说栈的内存回收不像java 没有复杂的垃圾回收机制。虽然同一个线程里会有多个栈帧,但同一时间只有一个栈帧,上面提到的-Xms指定的空间,其实是每个栈帧的空间。当线程中一个方法返回时,当前栈帧会传回此方法的执行结果给前一个栈帧,在方法返回之后,当前栈帧就随之被丢弃,前一个栈帧就重新成为当前栈帧了。

 

每个都有自己的:局部变量表、操作数栈、动态链接、返回地址。其中局部变量表和操作数栈的容量是在编译期确定,因此栈帧实际消耗容量的大小仅仅取决于Java虚拟机的实现和方法调用时可被分配的内存。

 

局部变量表:存放局部变量的列表,一个局部变量类型为booleanbytecharshortfloatreferencereturnAddress的数据,两个局部变量可以保存一个类型为longdouble的数据。局部变量使用索引来进行定位访问,第一个局部变量的索引值为零。

 

操作数栈:后进先出(Last-In-First-OutLIFO)栈,长度由编译期决定,在任意时刻,即任意一个栈帧中的操作数栈都会有一个确定的栈深度,一个long或者double类型的数据会占用两个单位的栈深度,其他数据类型则会占用一个单位深度。

 

动态链接:简单的理解为指向运行时常量池的引用。Class文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用(Symbolic Reference)来表示的,动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用。

 

返回地址:方法调用的返回,包括正常返回(有返回值)和异常返回(没有返回值),不同的返回类型有不同的指令。

 

5、本地方法栈

 

用于支持native方法,和java虚拟机栈相似,是线程私有,只是这个栈是采用其他语言实现。同样会有可能抛出StackOverflowErrorOutOfMemoryError异常。-Xss设置栈内存的大小同样适用于本地方法栈。

 

关于内存溢出和内存泄漏

 

内存泄漏一定会导致内存溢出,但内存溢出不一定是内存泄漏导致,也有可能是服务器内存本来就不足,可以通过增加服务器内存 同时增大-Xms Xmx配置。

 

内存泄漏一般比较隐蔽,难于发现。典型的发生场景就是,多线程的的线程中中使用ThreadLocal,在线程执行结束时没有remove,导致对象无法被回收,日积月累内存耗尽,抛出OutOfMemoryError异常。

 

关于jvm的内存结构就总结到这里,转载请注明出处:

http://moon-walker.iteye.com/blog/2394056

 

部分内容参考自周志明翻译的《java虚拟机规范SE7

  • 大小: 40 KB
0
0
分享到:
评论

相关推荐

    JVM 内存结构及配置总结

    1. **JVM内存结构** JVM内存主要分为以下几个区域: - **方法区(Method Area)**:这是所有线程共享的区域,存储类信息、常量、静态变量、即时编译后的代码等。在Java 8以前,这部分也被称为永久代(Permanent ...

    JVM内存结构.pdf

    ### JVM内存结构详解 #### 一、概述 Java虚拟机(JVM)作为Java程序的运行环境,其核心组件之一便是内存管理系统。理解JVM的内存布局对于开发高性能的应用程序至关重要。本文将详细介绍JVM内存结构及其各个组成部分...

    解析JVM内存结构和6大区域

    JVM 内存结构和 6 大区域 JVM 是 Java虚拟机,它是 Java 语言的核心组件之一,为 Java 程序提供了运行环境。JVM 的内存结构是 Java 程序的基础,它的设计和实现对 Java 程序的性能和可靠性产生了深远的影响。 JVM ...

    Jvm性能优化-JVM内存结构原理分析03

    "Jvm性能优化-JVM内存结构原理分析03" Jvm性能优化是Java虚拟机(JVM)中非常重要的一部分,它对Jvm的性能产生了很大的影响。本文将从Jvm内存结构的角度来分析Jvm性能优化的原理。 Jvm内存结构主要分为五部分:堆...

    JVM内存结构.zip

    JVM内存结构的理解对于优化Java程序性能、避免内存溢出等问题至关重要。以下是对JVM内存结构的详细阐述: 1. **堆内存(Heap)** 堆内存是Java程序中最大的一块内存区域,用于存储对象实例。所有通过`new`关键字...

    JVM内存结构笔记.rar

    总之,深入理解JVM内存结构及其管理机制,有助于我们编写更高效、更稳定的Java程序,并能有效地处理内存相关问题。这份"JVM内存结构笔记"将详细阐述这些内容,是学习和研究JVM内存管理的重要参考资料。

    JVM内存结构

    JVM内存结构的理解对于优化Java程序的性能至关重要。JVM内存主要分为以下几个区域: 1. **程序计数器(Program Counter Register)**:每个线程都有一个独立的程序计数器,用于存储当前线程正在执行的字节码指令...

    linux & JVM内存结构分析

    总结来说,Linux和JVM内存结构分析是提升系统效率和稳定性的重要手段。通过阅读和分析上述文件,我们可以深入了解系统资源的使用情况,进而进行针对性的调优。对于IT专业人士而言,掌握这些知识不仅能提升工作效率,...

    一文搞懂JVM内存结构

    了解JVM内存结构对于优化代码性能、防止内存泄漏以及理解程序运行时的行为至关重要。本文将深入探讨JVM内存的各个区域,帮助你全面掌握JVM的工作原理。 首先,JVM内存主要分为以下几个部分: 1. **程序计数器...

    JVM内存结构-JVM体系结构 程序计数器 虚拟机栈 本地方法栈 堆 方法区

    JVM内存结构-JVM体系结构 程序计数器 虚拟机栈 本地方法栈 堆 方法区

    深入理解JVM内存结构及运行原理全套视频加资料

    2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...

    Java虚拟机 JVM 内存结构介绍

    Java虚拟机(JVM)内存结构是理解Java...总之,JVM内存结构是Java平台的核心特性,它为程序提供了动态的内存分配和管理,确保了程序的稳定性和效率。深入理解这一结构,有助于开发者写出更高效、更健壮的Java应用程序。

    JVM内存结构、Java内存模型、Java对象模型1

    Java内存模型(JMM)与JVM内存结构不同,它是针对多线程环境下内存访问的抽象模型。JMM确保在多线程环境下,共享变量的读写操作具有正确的顺序和可见性,通过volatile、synchronized等关键字来实现这一目标。JMM关注...

    java jvm内存结构 调优

    java jvm内存结构 调优

    jvm内存基本结构及垃圾回收

    理解JVM内存结构和垃圾回收机制对于Java开发者至关重要,它可以帮助我们更好地优化程序性能,避免内存溢出等问题。通过调整JVM参数,如堆大小、新生代与老年代的比例、垃圾收集器的选择等,我们可以根据应用的需求...

    1. JVM 内存结构的组成、各部分功能作用,学会利用内存诊断工具排查内存相关问题;2. JVM 的招牌-jvm.zip

    了解JVM内存结构及其功能对于优化Java应用程序的性能和排查内存问题至关重要。 首先,我们来详细探讨JVM的内存结构。在Java中,内存主要分为以下几个区域: 1. **程序计数器(Program Counter Register)**:每个...

    JDK8的JVM内存结构,元空间替代永久代成为方法区及常量池的变化1

    在JDK8中,JVM内存结构发生了显著变化,尤其是元空间(MetaSpace)替代了永久代(Permanent Generation)作为方法区的一部分。这种方法区的调整是由于永久代存在的一些问题,比如大小设定困难,容易引发溢出,以及给...

    深入理解JVM内存结构及运行原理全套视频加资料.txt

    2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...

    jvm内存结构-机器指令的执行模式演示demo代码

    jvm内存结构-栈的变化,机器指令的格式/执行模式文章中的demo代码。

Global site tag (gtag.js) - Google Analytics