借用一句话:Java与C++之间有一堵内存动态分配和垃圾收集技术围成的高墙,墙外面的人想进来,墙里面的人却想出去。

一.我们为什么要了解JAVA内存

因为虚拟机帮我们JAVA程序员管理着内存,我们在new Object()申请了内存创建对象之后,便不需要再去delete/free来释放内存。也因此不容易出现内存泄漏和内存溢出的问题,看起来一切都很美好。

但是,如果一个程序员不了解虚拟机是怎么管理内存的,那么在排查内存相关的错误是便会成为一个巨大的难题。

二.内存区域有哪些

内存区域分为两种,一种随着虚拟机的进程启动而存在。另一种则依赖用户进程的启动和结束而建立和销毁。

1.程序计数器

一块较小的线程私有的内存空间,可以看作是当前线程的所执行的字节码的行号指示器。

如果线程正在执行的是一个JAVA方法,那么计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,那么计数器值为空(Undefined)。

该内存区域是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemoryError(OOM)情况的区域。

2.虚拟机栈

线程私有的,每个Java方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

3.本地方法栈

线程私有,同虚拟机栈,为native方法服务。在HotSpot虚拟机中,直接把虚拟机栈和本地方法栈合二为一。

4.堆

线程共享的区域。存放实例的区域,几乎所有的对象实例都在这里分配内存。同时,因为空间固定,而用户可能需要不断生成实例,故该区域还是垃圾收集的主要区域。垃圾收集将在后面提到。

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

5.方法区

线程共享的区域,存储已被虚拟机加载的类信息、常量、静态变量等数据。

很多人称之为“永生代”,因为HotSpot使用永生代来实现方法区。Java规范中对方法区的限制十分宽松,可以选择不实现垃圾收集。

6.运行时常量池

方法区的一部分,用于存放编译器生成的各种字面量和符号引用,在类加载完成后进入方法区的运行时常量池中存放。

关于这快区域,有一个需要注意的地方。代码如下:

public classt18 {public static voidmain(String[] args){

Integer a1= 128;

Integer a2= 128;

System.out.println(a1==a2);

Integer b1= 127;

Integer b2= 127;

Integer b3= 1 +b1;

Integer b4= a1 -1;

System.out.println(b1==b2);

System.out.println(b3==a1);

System.out.println(b4==b1);

}

}

上面代码的运行结果为 false ,true ,false ,true 。这是很多人第一次见到时都无法理解的,因为这里涉及到了常量池的知识。JVM会把一些int,String等数据进行在常量池中缓存,但是重点在于,对于int型数据,只会缓存 -128~127 范围内的数据。因此:

a1、a2超过了127,在堆中分配内存,两者指向不同对象,返回false;

b1、b2都指向常量池中的127,故b1、b2指向地址相同,返回true;

第3、4个同理,Integer b3 = 1+b1  ----> Integer b3 =Integer.valueOf(1+b1)。

7.直接内存

JDK1.4后加入了NIO (new I/O)类,引入了基于通道与缓冲区的IO方式,可以使用native函数库分配机器内存,如电脑8g内存,JVM可以使用电脑的剩余内存,只需要在java堆中存储DirectByteBuffer对象作为内存的引用进行操作。这样在某些场景中提高性能。

三.在new一个对象时发生了什么

当虚拟机遇到一条new 指令时,首先回去检查能否在常量池中定位,并检查这个类是否已经被加载、解析、初始化过,如果没有,那么必须先执行类的加载过程。

类加载完成后,接下来将会为对象分配内存,即把一块确定大小的内存从java堆中划分出来。如果java堆是连续且规整的,已分配过的内存放在一边,空闲的在另一边。中间的指针作为分界点的指示器,那么分配内存就是将指针向空闲的方向移动所需要的距离,(使用Serial、PalNew等带规整过程的垃圾收集器);如果java堆是不规整的,那么虚拟机就必须维护一个记录,分配内存的同时需要更新记录,(如使用CMS这种基于标记-清除算法的收集器)。

将分配到的内存空间赋予初值,如整形变量置0,bool型置false。保证了对象字段在代码中可以不付初值就可以直接使用。然而在实际编写代码中,建议采用赋初值的形式,保持一个良好的代码习惯。另外

String s ;

System.out.println(s); //未初始化,编译器报错

该代码会报错,而不是输出null,切记切记。

初始化对象的对象头数据,每个java对象都有对象头(Object Header),里面记录了对象是哪个类的实例、如何找到类的元数据信息、哈希码、GC年龄、偏向锁等信息。

执行init方法,把对象按照程序员的一员进行初始化,这样,一个可用的对象才完成new操作。

四.一个对象在内存中有哪些部分

以HotSpot虚拟机为例,对象在内存中存储的区域可以分为三个部分

1.对象头(Object Header)

对象头包括两部分,一部分用于存储对象自身的运行时数据,官方称之为“Mark Word”,包括:HashCode、GC年龄、锁状态、线程持有锁、偏向锁线程id、偏向时间戳等。占一个字长(32bit或64bit,取决于虚拟机)。

另一部分是类型指针,对象指向的类元数据指针,通过这个来确定该对象是哪个类的实例。另外如果一个对象是一个数组,那么还有一块用于记录数组长度的数据。

2.对象数据

即实例中存储的,程序员设计的应该存储的数据。

3.对齐填充

不是必须的,仅仅起着占位的作用,HotSpot内存管理规定对象的起始地址必须是8字节的整数倍,换句话说对象的大小必须是9字节的整数倍,因此,当实例大小没有对齐时,需要通过对齐填充来补全。

五.如何访问定位对象

创建对象是为了使用对象,java虚拟机使用栈上的reference数据来操作堆上的具体对象,目前的访问方式主流有两种:

1.使用句柄访问

Java堆中会划分出一块内存作为句柄池,reference中存储的是对象的句柄地址,而句柄中包含了对象的实例数据与类型数据各自的地址信息。

即访问时refenrence(存句柄地址) --> 句柄池(堆中,存对象地址) --> 具体对象(堆或方法区中)。

2.使用直接指针访问

直接访问,reference(存对象地址)-->具体对象(堆中或方法区中),一次跳转。HotSpot虚拟机使用的就是这种方式。

java 内存指针_java内存模型详解相关推荐

  1. java 对象工厂_Java工厂模型详解

    工厂模型:工厂类根据传入的参数,创造出对应的产品对象. 工厂模型特点: 是用来批量的产生对象. 创建步骤: 创建一个接口: 创建接口,不同的实现类: 创建工厂,产生不同实现类的对象: 工厂模型案例: ...

  2. java super实例_java Super 用法详解及实例代码

    java Super 用法详解及实例代码 发布于 2021-1-8| 复制链接 摘记: java  Super 用法详解 1)有人写了个很好的初始化属性的构造函数,而你仅仅想要在其中添加另一些自己新建 ...

  3. java异常体系_JAVA异常体系结构详解

    一.什么是异常 异常:程序在运行过程中发生由于硬件设备问题.软件设计错误等导致的程序异常事件.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个异常对象.      --百 ...

  4. java linkedlist 节点_JAVA学习-LinkedList详解

    1.定义 实现List接口与Deque接口双向链表,实现了列表的所有操作,并且允许包括null值的所有元素,对于LinkedList定义我产生了如下疑问: 1.Deque接口是什么,定义了一个怎样的规 ...

  5. java enum 变量_java枚举使用详解

    package com.ljq.test; /** * 枚举用法详解 * * @author jiqinlin * */ public class TestEnum { /** * 普通枚举 * * ...

  6. java list用法_Java List 用法详解及实例分析

    Java List 用法详解及实例分析 Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解. List:元素是有序的(怎么存的就怎么取出来,顺 ...

  7. java abstract 变量_Java基础知识详解:abstract修饰符

    abstract详解 abstract一般用来修饰类和方法. 1.abstract修饰类 abstract修饰类,会使得类变成抽象类,抽象类不能生成实例,但是可以作为对象变量声明的类型,也就是编译时类 ...

  8. java 正则 实例_Java正则表达式实例详解

    创建正则表达式 你可以从比较简单的东西入手学习正则表达式.要想全面地掌握怎样构建正则表达式,可以去看JDK 文档的java.util.regex 的Pattern 类的文档. 字符 B 字符B \xh ...

  9. java cas机制_Java CAS机制详解

    CAS目的: 在多线程中为了保持数据的准确性,避免多个线程同时操作某个变量,很多情况下利用关键字synchronized实现同步锁,使用synchronized关键字修可以使操作的线程排队等待运行,可 ...

最新文章

  1. Linux 黑话解释:什么是定时任务
  2. linux主机释放cache和buffer
  3. 几种常见的数据库连接方法
  4. wxWidgets:wxGenericDirCtrl类用法
  5. JAVA模拟肯德基点餐系统源码,肯德基辞职员工透露,用这几句暗语点餐,你拿的分量会比别人多...
  6. 计算机专业科普知识图文并茂,科普下计算机专业需要什么基础知识?
  7. GraphQL query的schema校验
  8. 使用css绘制小三角
  9. ORM框架SQLAlchemy使用学习
  10. 华为Mate 50系列明年亮相:或首发高通骁龙898 4G芯片
  11. Javsscript自定义事件和触发
  12. 某些特殊悼念日的时候,让个人网页风格变黑灰色
  13. js12---闭包,原型,继承
  14. wifi mesh 开关
  15. 【考研】计算机考研,我不建议考408
  16. php json输出对象的属性值,JavaScript_jquery动态遍历Json对象的属性和值的方法,1、遍历 json 对象的属性/ - phpStudy...
  17. 万向节锁--简单解释
  18. 小陈的Java学习心得
  19. 多人 协作 任务 android 软件,MeisterTask(团队协作软件)
  20. VM VMware Workstation与 Device/Credential Guard 不兼容 Windows下解决方法

热门文章

  1. wsl2启动桌面_WSL2使用xrdp实现图形桌面
  2. python基础装饰器(二十)
  3. java插入排序算法实现
  4. pythoncsv数据类型_python – 从CSV文件行中的值确定数据类型
  5. html label 两端对齐,如果实现表单的label文字两端对齐
  6. 重构是什么、为什么要重构
  7. 记录:注意Unity返回的所有数组,只是数组的副本
  8. OpenShift 4 - 为Gogs构建一个Operator
  9. OpenShift 4 - Knative教程 (7) Eventing之Broker和Trigger
  10. OpenShift 4 之Istio-Tutorial (8) 在服务之间配置Mutual TLS双向传输安全