JVM之Java内存区域介绍

JAVA内存区域

一.概述

正是因为虚拟机的自动内存机制,所以我们不需要像c++程序员一样,进行手动释放,不容易出现内存泄漏和溢出的问题。然而因为是虚拟机自动控制,当出现内存泄漏方面的问题,如果对虚拟机不了解,会带来十分的痛苦。
Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。
Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

二.运行时数据区域

jdk1.8之前:
在这里插入图片描述

jdk1.8:
下图·引用自一个JavaGuide博主:

在这里插入图片描述

2.1程序计数器

这块区域是每个线程独立拥有的,也就是线程私有的,我们可以把它看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选举下一个要执行的字节码指令,其他,比如分支,循环,跳转,异常处理等也要依赖这个计数器
我们还需注意的是,为了线程切换后可以恢复到正确的执行的位置,每个线程需要需要一个独立的程序计数器,各线程之间计数器不影响,独立存储,这类内存区域就是"线程私有"的内存。
程序计数器的作用:
1. 字节码解释器通过改变程序计数器来读取下一个指令,实现分支,循环,跳转,异常处理等操作。
2. 如果在多线程运行的情况下,程序计数器可以记录当前线程执行的位置。

另外,我们还需注意:**这块区域是虚拟机规范里面唯一一个没有规定任何OutOfMemoryError情况的区域。**它的生命周期随着线程的创建毁灭而变化.

2.2 Java虚拟机栈

虚拟机栈也是线程私有的,生命周期与线程相同,描述方法执行的内存模型,方法中调用的数据是通过栈传递。
Java中的内存可以分为栈内存和堆内存,栈就是虚拟机栈。也可以说是虚拟机栈中的局部变量部分。
每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
局部变量表主要存放了编译器可知的各种数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

2.3 本地方法栈

本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为
Native 方法服务, 如果一个 JVM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个
C 栈,但 HotSpot JVM 直接就把本地方法栈和虚拟机栈合二为一。

2.4 堆 (Heap-线程共享)-运行时数据区

是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行
垃圾收集的最重要的内存区域。由于现代 JVM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。

2.5 方法区 /永久代(线程共享)

方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久带这一说法。
即我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、常量、静
态变量、即时编译器编译后的代码等数据. HotSpot VM把GC分代收集扩展至方法区, 即使用Java
堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存,
而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型
的卸载, 因此收益一般很小)。

2.6运行时的常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。

既然运行时常量池时方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

三.HotSpot虚拟机对象

3.1对象的创建

JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这
五个过程。在这里插入图片描述

a.加载
加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的 java.lang.Class 对
象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个 Class 文件获取,这里既
可以从 ZIP 包中读取(比如从 jar 包和 war 包中读取),也可以在运行时计算生成(动态代理),
也可以由其它文件生成(比如将 JSP 文件转换成对应的 Class 类)。

b.验证
这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并
且不会危害虚拟机自身的安全。

c.准备
准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使
用的内存空间。注意这里所说的初始值概念,比如一个类变量定义为:
public static int v = 8080;
实际上变量 v 在准备阶段过后的初始值为 0 而不是 8080,将 v 赋值为 8080 的 put static 指令是
程序被编译后,存放于类构造器方法之中。
但是注意如果声明为:
public static final int v = 8080;
在编译阶段会为 v 生成 ConstantValue 属性,在准备阶段虚拟机会根据 ConstantValue 属性将 v
赋值为 8080。

d.解析
解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是 class 文件中
的:

  1. CONSTANT_Class_info
  2. CONSTANT_Field_info
  3. CONSTANT_Method_info
    等类型的常量。

e.初始化
初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载
器以外,其它操作都由 JVM 主导。到了初始阶段,才开始真正执行类中定义的 Java 程序代码。

四.String类和常量池

String 对象的两种创建方式:

	String str1 = "grace";//首先先在常量池中检查是否存在,若不存在,则创建对象
	String str2 = new String("grace");//堆中创建一个新的对象
	String str3 = new String("grace");//堆中创建一个新的对象
	System.out.println(str1==str2);//false
	System.out.println(str2==str3);//false

常量池使用的方法

  1. ""表示的出现在常量池中

  2. 使用String的intern方法,它的作用:如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用。

      	String s1 = new String("grace");
           String s2 = s1.intern();
           String s3 = "grace";
           System.out.println(s2);//grace
           System.out.println(s1 ==s2)//false
           //因为一个在堆中创建,一个在常量池中
           System.out.println(s3==s2);//true 都是从常量池中读取
    

注意:

  1. 凡是通过new的,都在堆中创建了对象
  2. 有变量参与运算的也会在堆中创建对象

五.8中基本类型的包装类和常量池

Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;这 5 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。
两种浮点数类型的包装类 Float,Double 并没有实现常量池技术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值