面试JVM(一)内存分化

6 篇文章 0 订阅

参考书:深入理解java虚拟机 JVM高级特性与最佳实践

https://blog.csdn.net/stanlee_0/article/details/51171382点击打开链接

http://www.importnew.com/18961.html点击打开链接

 

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。其中有些区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。下图左边是所有线程共享区域,右边是线程私有区域

 

 

程序计数器

 

 

记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。

java的执行顺序可能和书写顺序不一致。

        在java的字节码解析器当中,需要辨别当前的字节码解析到了哪个地方,同时需要来控制程序的流程,如果在程序当中没有一个东西来记录当前程序执行到哪个,同时下一步应该执行哪一步操作例如:分支、循环、跳转、异常处理等操作都不是按照原本程序书写的顺序来执行的,所以为了能够引导程序的运行,就需要引进一个用来引导字节码解析顺序的东西,就叫做程序计数器。

        程序计数器内存空间较小,主要记录下一条需要执行的字节码的位置。Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的(时分复用),因此在任何一个确定的时刻,一个cpu只会处理一个线程中的指令。因此,为了保证线程切换后恢复正确的执行位置,每个线程都持有一个独立的程序计数器,所以,程序计数器是线程私有的内存。如果正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是Native方法(JAVA的native方法适用的情况:为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问。为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。),这个计数器值为空(null)。而且程序计数器区域不会发生OutOfMemeoryError(内存溢出异常)。

 

虚拟机栈

每个 Java 方法在执行的同时会创建一个栈帧用于存储 局部变量表、操作数栈、常量池引用等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。

        栈是线程私有的,其生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧。(栈里面是一个个的栈帧栈帧是方法调用时产生的一种数据结构,)栈帧包括局部变量表,操作数栈,动态链接,方法出口等信息。每个方法调用直至完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

其中栈帧的局部变量表中存放了编译期可知的各种基本数据类型、对象引用(reference类型,一般是指针,指向对象的位置)和returnAddress类型。

栈有两种异常情况:线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常(这种可能是递归太深,栈中无法再容纳下一个栈帧);虚拟机栈可以动态扩展,如果无法申请到足够的内存,就会抛出OutOfMemoryError异常。

 

 

 

本地方法栈

本地方法不是用 Java 实现,对待这些方法需要特别处理。

        本地方法栈与Java虚拟机栈类似,不同的是Java虚拟机栈为执行Java方法服务,而本地方法栈为虚拟机执行Native方法服务,所以有些虚拟机直接将这两个栈合二为一。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

 

所有对象实例都在这里分配内存。是垃圾收集的主要区域("GC 堆"),

Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的)。

现代的垃圾收集器基本都是采用分代收集算法,该算法的思想是针对不同的对象采取不同的垃圾回收算法,因此虚拟机把 Java 堆分成以下三块:新生代(Young Generation)老年代(Old Generation)永久代(Permanent Generation)。当一个对象被创建时,它首先进入新生代,之后有可能被转移到老年代中。新生代存放着大量的生命很短的对象,因此新生代在三个区域中垃圾回收的频率最高。为了更高效地进行垃圾回收,把新生代继续划分成以下三个空间:Eden,From Survivor,To Survivor。

 

     Java堆一般来说是Java虚拟机管理的内存中最大的一块。Java堆是所有线程共享的内存区域,其伴随这虚拟机进程而生,用来存放对象实例。java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。但是随着JIT编译器的发展,栈上分配等技术会使所有对象都分配在堆上变得不那么“绝对”。

Java堆是垃圾收集器管理的主要区域,Java堆还可以细分为新生代和老年代,再细致一点新生代还可以分为Eden空间,FromSurvivor 空间和ToSurvivor空间。如果从内存分配的角度来看,线程共享的java堆中可以划分出多个线程私有的分配缓冲区。

        堆的大小可以通过Xms和Xmx参数来控制。如果堆中没有内存来分配实例,并且无法扩展的时候,将会抛出OutOfMemoryError异常。

java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。

 

方法区

线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

和 Java 堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。

对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。

JDK 1.7 之前,HotSpot(热部署) 虚拟机把它当成永久代来进行垃圾回收,JDK 1.8 之后,取消了永久代,用 metaspace(元数据)区替代。

运行时常量池(Runtime ConstantPool)

运行时常量池是方法区的一部分。用于存放编译器生成的各种字面量(字符串常量)和符号引用。

Class 文件中除了有类的版本,字段,方法,接口等信息外,还有一项信息是常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域(方法区的运行常量池)。

除了在编译期生成的常量,还允许动态生成(具备动态性,java语言并不要求常量只能在编译期产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行时期也可能将新的常量放入池中),例如 String 类的 intern()(intern() 方法返回字符串对象的规范化表示形式)。这部分常量也会被放入运行时常量池。常量池内存溢出为OutOfMemoryError。

 

直接内存

在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

 

 

 

 

 

 

 

 

1.      堆中存储的都是对象信息,每一个对象都有一个class字节码文件与之对应

2.      堆中的数据是线程之间共享的,他只保存对象实例本身,而不保存引用类型,比如A a = new A(),引用类型的a是保存在栈中的,而A实例本身是保存在堆中的。

 

方法区

方法区是线程共享的,保存了类的源信息,举几个例子:

1.      类的完整类名

2.      类的父类的完整类名

3.      类的类型修饰符(public,final,abstract….)

4.      实现的接口interface的有序列表

5.      域(Field)的信息,比如说成员变量之类的

6.      方法(Method)信息,就是在类中创建的方法的,比如方法名,方法的修饰符,方法的返回类型,有序的方法的参数列表

7.      静态变量信息

8.      类型常量池信息,不过String常量池在JDK7的时候就从方法区变为堆了

1.      栈是线程私有的,是运行时创建的,每启动一个线程,就创建一个栈。栈存储的都是当前线程运行时相关的信息,和堆区别一点就是堆是线程共享的,是用来存储数据的。栈相当于处理逻辑,而堆相当于数据。

2.      通俗易懂的说,栈中存放着基本数据类型和引用类型

3.      栈是由一个一个组成的,因此JAVA中的栈也被称为帧栈

4.      帧保存一个方法里的局部变量表操作数栈常量池指针

5.      每一次方法调用,就创建一个帧,然后向栈内压入,这个帧包含了调用的方法的相关信息

6.      局部变量表:存储了方法相关的局部变量,这个参数是一个非常宽泛的概念,不仅仅是方法体内部的局部变量,还有调用方法需要传入的参数。我们可以通过索引的方式访问到局部变量表中存储的参数。

7.      操作数栈:和局部变量表不同,操作数栈不是依靠索引来访问的,他是通过入栈和出栈来操作的。也就可以理解为,操作数栈只是一个数据进行存储计算时的临时存储数据的区域。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值