JAVA内存结构详解

Java把内存分成:栈内存,堆内存,方法区,本地方法区和寄存器等。

  下面分别介绍栈内存,堆内存,方法区各自一些特性:

  1、栈内存

  (1)一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。

  (2)每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

  (3)栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

  (4)当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

  (5)当数据使用完,所占空间会自动释放。

  2、堆内存

  (1)堆内存用于存放由new创建的对象和数组。

  (2)每一个实体都有一个内存地址值

  (3)实体中的变量都有默认初始化值

  (4)实体不再被使用,会在不确定的时间内被垃圾回收器回收

  补充:数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!

  3、方法区

  1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。

  2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

  方法区存放装载的类数据信息包括:

  (1)基本信息:

  1)每个类的全限定名

  2)每个类的直接超类的全限定名(可约束类型转换)

  3)该类是类还是接口

  4)该类型的访问修饰符

  5)直接超接口的全限定名的有序列表

  (2)每个已装载类的详细信息:

  1)运行时常量池:

  存放该类型所用的一切常量(直接常量和对其它类型、字段、方法的符 号引用),它们以数组形式通过索引被访问,是外部调用与类联系及类型对象化的桥梁。它是类文件(字节码)常量池的运行时表示。(还有一种静态常量池,在字节码文件中)。

  2)字段信息:

  类中声明的每一个字段的信息(名,类型,修饰符)。

  3)方法信息:

  类中声明的每一个方法的信息(名,返回类型,参数类型,修饰符,方法的字节码和异常表)。

  4)静态变量

  5)到类 classloader 的引用:即到该类的类装载器的引用。

  6)到类 class 的引用:  虚拟机为每一个被装载的类型创建一个 class 实例, 用来代表这个被装载的类

  以上为栈内存,堆内存,方法区的一些特性,其中

  栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:


int a = 5;

int b = 5;

  编译器先处理int a = 5;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有5这个值,如果没找到,就将5存放进来,然后将a指向5。

  接着处理int b = 5;在创建完b的引用变量后,因为在栈中已经有5这个值,便将b直接指向5。这样,就出现了a与b同时均指向5的情况。

  这时,如果再令a=8;那么编译器会重新搜索栈中是否有8值,如果没有,则将8存放进来,并令a指向8;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

  注意:这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

  下面举例说明Java程序在内存中的分配:

  拿String举例,其中String是一个特殊的包装类数据。可以用:


  String str = new String("abc");

  String str = "abc";

  两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。

  而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向"abc",如果已经有"abc" 则直接令str指向"abc"。

  比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。


  String str1 = "abc";

  String str2 = "abc";

  System.out.println(str1==str2); //true

  //可以看出str1和str2是指向同一个对象的。

  String str1 =new String ("abc");

  String str2 =new String ("abc");

  System.out.println(str1==str2); // false

  //用new的方式是生成不同的对象。每一次生成一个。

  因此用第一种方式创建多个"abc"字符串,在内存中其实只存在一个对象而已.这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建对象。

  而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是有必要创建新对象,从而加重了程序的负担。

  另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。当心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。

  内存图演示

  new String ("abc")内存图:

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-10-29 23:02:01

JAVA内存结构详解的相关文章

Oracle内存结构详解(一) Oracle SGA简介

Oracle的内存配置与oracle性能息息相关.关于内存的配置,是最影响Oracle性能的配置.内存还直接影响到其他两个重要资源的消耗:CPU和IO. 先看Oracle内存存储的主要内容是什么: 程序代码(PLSQL.Java): 关于已经连接的会话的信息,包括当前所有活动和非活动会话: 程序运行时必须的相关信息,例如查询计划: Oracle进程之间共享的信息和相互交流的信息,例如锁: 那些被永久存储在外围存储介质上,被cache在内存中的数据(如redo log条目,数据块). 每个Orac

Oracle内存结构详解(五) Oracle PGA

PGA(Program Global Area程序全局区)是一块包含一个服务进程的数据和控制信息的内存区域.它是Oracle在一个服务进程启动是创建的,是非共享的.一个Oracle进程拥有一个PGA内存区.一个PGA也只能被拥有它的那个服务进程所访问,只有这个进程中的Oracle代码才能读写它.因此,PGA中的结构是不需要Latch保护的. 我们可以设置所有服务进程的PGA内存总数受到实例分配的总体PGA(Aggregated PGA)限制. 在专有服务器(Dedicated Server)模式

Java内存溢出详解

一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError: Java heap space----JVM Heap(堆)溢出JVM在启动的时候会自动设置JVM Heap的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存. 可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置.Heap的大小是Young Generation 和Tenured Generaion 之和. 在JVM中如果98%的时间是用于GC

Oracle内存结构详解(六)UGA、CGA及软件代码区

1.UGA (The User Global Area) PGA是一段包含一个Oracle服务或后台进程的数据和控制信息的内存.PGA的大小依赖与系统的配置.在专用服务(Dedicated Server)模式下,一个服务进程与一个用户进程相关,PGA就包括了堆空间和UGA.而UGA(User Global Area用户全局区)由用户会话数据.游标状态和索引区组成.在共享服务(MTS)模式下,一个共享服务进程被多个用户进程共享,此时UGA是Shared Pool或Large Pool的一部分(依赖

Oracle内存结构详解(四) Oracle SGA其他组成部分

1.Redo Log Buffer Redo Log Buffer是SGA中一段保存数据库修改信息的缓存.这些信息被存储在重做条目(Redo Entry)中.重做条目中包含了由于INSERT.UPDATE.DELETE.CREATE.ALTER或DROP所做的修改操作而需要对数据库重新组织或重做的必须信息.在必要时,重做条目还可以用于数据库恢复. 重做条目是Oracle数据库进程从用户内存中拷贝到Redo Log Buffer中去的.重做条目在内存中是连续相连的.后台进程LGWR负责将Redo

Oracle内存结构详解(三) Oracle管理Share Pool

SGA中的共享池由库缓存(Library Cache).字典缓存(Dictionary Cache).用于并行执行消息的缓冲以及控制结构组成. Shared Pool的大小由参数SHARED_POOL_SIZE决定.9i中,在32位系统下,这个参数的默认值是8M,而64位系统下的默认值位64M.最大为4G. 10g 以后可以通过SGA_TARGET 参数来自动调整. 对于Shared Pool的内存管理,是通过修正过的LRU算法表来实现的. 1.库缓存(Library Cache) Librar

Oracle内存结构详解(二) Oracle管理Buffer Cache

Buffer Cache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域.Oracle进程如果发现需要访问的数据块已经在buffer cache中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能(内存的读取效率是磁盘读取效率的14000倍).Buffer cache对于所有oracle进程都是共享的,即能被所有oracle进程访问. 和Shared Pool一样,buffer cache被分为多个集合,这样能够大大降低多CPU系统中的争用问题. 1.Buffer c

基于Java中字符串内存位置详解_java

前言 之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前只记得String是一个不变的量,应该是要存放在常量池里面的,但是后来问到new一个String出来应该是放到哪里的,这个应该是放到堆里面的,后来又问到String的引用是放在什么地方的,当时傻逼的说也是放在堆里面的,现在总结一下:基本类型的变量数据和对象的引用都是放在栈里面的,对象本身放在堆里面,

Java直接(堆外)内存使用详解

本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存的朋友,提供点快捷的参考. 数据类型 下面这些,都是在使用DirectBuffer中必备的一些常识,暂作了解吧!如果想要深入理解,可以看看下面参考的那些博客. 基本类型长度 在Java中有很多的基本类型,比如: byte,一个字节是8位bit,也就是1B short,16位bit,也就是2B int,32位bit