一、了解java的内存大致划分

栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中。

堆:存放用new产生的数据

静态域:存放在对象中用static定义的静态成员

常量池:存放常量

二、Java内存分配中的栈

在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。

当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

三、Java内存分配中的堆

堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。  引用变量就相当于是 为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序 运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。

实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针!

四、常量池 (constant pool)

常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:

◆类和接口的全限定名;

◆字段的名称和描述符;

◆方法和名称和描述符。

对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值。

在程序执行的时候,常量池 会储存在Method Area,而不是堆中。

五、堆和栈

Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。

栈的优势是,存取速度比堆要快,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是 确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int, short, long,byte, float, double, boolean, char)。

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

int a = 3;

int b = 3;

编译器先处理int a =3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b =3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。

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

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

String是一个特殊的引用类型的数据类型,可以用:

String str = new String("abc");

String str = "abc";

两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对String类的对象引用变量str,然后通过符号引用去字符串常量池 里找有没有"abc",如果没有,则将"abc"存放进字符串常量池 ,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

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

Java代码

String str1 ="abc";

String str2 = "abc";

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

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

Java代码

String str1 =newString ("abc");

String str2 =newString ("abc");

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

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

因此用第一种方式创建多个”abc”字符串,在内存中 其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = newString("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

由于String类的immutable(不变)性质,当String变量需要经常变换 其值时,应该考虑使用StringBuffer类,以提高程序效率。

1. 首先String不属于8种基本数据类型,String是一个对象。因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。

2. new String()和newString(”")都是申明一个新的空字符串,是空串不是null;

3. String str=”kvill”;String str=new String(”kvill”)的区别

示例:

Java代码

String s0="kvill";

String s1="kvill";

String s2="kv"+"ill";

System.out.println( s0==s1 ); //true

System.out.println(s0==s2 );//true

Java内存分配与管理是Java的核心技术之一,之前我们曾介 今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识。一般Java在内存分配时会涉及到以下区域:

首先,我们要知结果为道Java 会确保一个字符串常量只有一个拷贝。

因为例子中的 s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字 符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中” kvill”的一个引用。所以我们得出s0==s1==s2;用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

示例:

Java代码

String s0="kvill";

String s1=newString("kvill");

String s2="kv"+ newString("ill");

System.out.println( s0==s1 );

System.out.println( s0==s2 );

System.out.println( s1==s2 );

例2中s0还是常量池 中"kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分 newString(”ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的引用;明白了这些也就知道为何得出此结果了。

四、String.intern()

再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的 intern()方法就是扩充常量池的 一个方法;当一个String实例str调用intern()方法时,Java 查找常量池中 是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常 量池中增加一个Unicode等于str的字符串并返回它的引用;看示例就清楚了

Java代码

String s0="kvill";

String s1=newString("kvill");

String s2=newString("kvill");

System.out.println( s0==s1 );

System.out.println( "**********");

s1.intern();

s2=s2.intern(); //把常量池中"kvill"的引用赋给s2

System.out.println( s0==s1);

System.out.println( s0==s1.intern() );

System.out.println(s0==s2 );

一个常见的误解:“使用 String.intern() 方法则可以将一个 String 类的保存到一个全局 String 表中 ,如果具有相同值的 Unicode 字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中”如果我把他说的这个全局的 String 表理解为常量池的话,他的最后一句话,”如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:

示例:

Java代码

String s1=new String("kvill");

String s2=s1.intern();

System.out.println( s1==s1.intern() );

System.out.println( s1+" "+s2 );

System.out.println( s2==s1.intern() );

在这个类中我们没有声名一个”kvill”常量,所以常量池中一开始是没有”kvill”的,当我们调用s1.intern()后就在常量池中新添加了一 个”kvill”常量,原来的不在常量池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了。

s1==s1.intern() 为false说明原来的”kvill”仍然存在;s2现在为常量池中”kvill”的地址,所以有s2==s1.intern()为true。

5. 关于equals()和==:

这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是 比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。

6. 关于String是不可变的

String的实例一旦生成就不会再改变了,比如说:Stringstr=”kv”+”ill”+” “+”ans”; 就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” ” 生成 “kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的”不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原 因了,因为StringBuffer是可改变的。

下面是一些String相关的常见问题:

String中的final用法和理解

Java代码

final StringBuffer a = newStringBuffer("111");

final StringBuffer b = newStringBuffer("222");

a=b;//此句编译不通过

final StringBuffer a = newStringBuffer("111");

a.append("222");// 编译通过

可见,final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象 的变化,final是不负责的。

String常量池问题的几个例子

下面是几个常见例子的比较分析和理解:

Java代码

String a = "a1";

String b = "a" + 1;

System.out.println((a== b));

String a ="atrue";

String b = "a" + "true";

System.out.println((a== b));

String a ="a3.4";

String b = "a" + 3.4;

System.out.println((a == b));

分析:JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。

Java代码

String str1 = "ab";

String str2 = "b";

String str = "a" + str2;

System.out.println((str1== str));

分析:JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" +bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给str2。所以上面程序的结果也就为false。

Java代码

String a = "ab";

final String bb = "b";

String b = "a" + bb;

System.out.println((a == b)); //result = true

分析:和[3]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以此时的"a" +bb和"a" +"b"效果是一样的。故上面程序的结果为true。

Java代码

String a = "ab";

final String bb = getBB();

String b = "a" + bb;

System.out.println((a == b));

Public static String getBB() {  return "b";   }

分析:JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为str2,故上面 程序的结果为false。

通过上面4个例子可以得出得知:

String  s  = "a" + "b" + "c";

就等价于String s = "abc";

String  a  = "a";

String  b  = "b";

String  c  = "c";

String  s  =  a  +  b  +  c;

这个就不一样了,最终结果等于:

Java代码

StringBuffer temp = new StringBuffer();

temp.append(a).append(b).append(c);

String s = temp.toString();

由上面的分析结果,可就不难推断出String 采用连接运算符”+”效率低下原因分析,形如这样的代码:

Java代码

publicclassTest {

publicstaticvoidmain(String args[]) {

String s = null;

for(inti =0; i <100; i++) {

s += "a";

}

}

}

每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。

String对象的intern方法理解和分析:

Java代码

publicclassTest4 {

privatestaticString a ="ab";

publicstaticvoidmain(String[] args){

String s1 = "a";

String s2 = "b";

String s = s1 + s2;

System.out.println(s == a); //FALSE

System.out.println(s.intern() == a);//true

}

}

这里用到Java里面是一个常量池的问题。对于s1+s2操作,其实是在堆里面重新创建了一个新的对象,s保存的是这个新对象在堆空间的的内容,所 以s与a的值是不相等的。而当调用s.intern()方法,却可以返回s在常量池中的地址值,因为a的值存储在常量池中,故s.intern和a的值相等。

总结

栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容

堆中存放使用new关键字创建的对象.

字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

静态成员变量在类装载的时候就进行了创建,在整个程序结束时按序销毁。

实例变量在类实例化对象时候创建,在对象销毁的时候销毁。

局部变量在局部范围使用时创建,跳出局部范围销毁。

java 内存详解_Java内存详解相关推荐

  1. java合法的数组声明_Java数组详解

    Java数组详解 数组详解 1.数组概述 数组是相同类型数据的有序集合 每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们 2.数组声明和创建 ①数组声明形式 dataType[] a ...

  2. java内存区域与内存溢出异常_Java内存区域与内存溢出异常

    Java的内存管理是一个老生常谈的问题,虽然Java号称可以自动管理自己的内存,使程序员从内存管理的围墙解放出来,但是一连串的内存泄漏和溢出方面的问题,使得我们不得不去深入了解Java的内存管理机制. ...

  3. java 获取内存使用情况_Java内存使用情况查看工具

    Java通过jvm自己管理内存,同时Java提供了一些命令行工具,用于查看内存使用情况. 这里主要介绍一下jstat.jmap命令以及相关工具. 一.jstat查看 gc实时执行情况 jstat命令命 ...

  4. java double储存原理_Java内存分配原理

    Java内存分配与管理是Java的核心技术之一,一般Java在内存分配时会涉及到以下区域: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 ...

  5. java内存模型 原子性_Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)...

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  6. java 内存屏障类型_Java内存模型精讲

    1.JAVA 的并发模型 共享内存模型 在共享内存的并发模型里面,线程之间共享程序的公共状态,线程之间通过读写内存中公共状态来进行隐式通信 该内存指的是主内存,实际上是物理内存的一小部分 2.JAVA ...

  7. java和硬件交互_Java内存模型

    Java内存模型 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存模式是一种虚拟机规范,真实并不存在 Java虚拟机规范中定义了Java内存模型(Java Memory Model,JM ...

  8. java内存分析工具_java内存查看与分析

    业界有很多强大的java profile的工具,比如Jporfiler,yourkit,这些收费的东西我就不想说了,想说的是,其实java自己就提供了很多内存监控的小工具,下面列举的工具只是一小部分, ...

  9. java 溢出与泄露_Java内存溢出与栈溢出

    一.背景知识 1.JVM体系结构 2.JVM运行时数据区 3.JVM内存模型 JVM运行时内存 = 共享内存区 + 线程内存区 3-1.共享内存区 共享内存区 = 持久带 + 堆 持久带 = 方法区 ...

最新文章

  1. JEESZ分布式框架之技术介绍文档
  2. 数据结构和算法解析:排序问题简易总结
  3. Timus 1204 Idempotents
  4. Java 基本类型相互转换
  5. php super和parent,parent()、parents()的用法区别
  6. 【Pytorch神经网络实战案例】03 CIFAR-10数据集:Pytorch使用GPU训练CNN模版-测试方法
  7. python 加权随机算法_加权随机算法 - 飞儿飞的个人空间 - OSCHINA - 中文开源技术交流社区...
  8. OpenCV3学习(11.3)关键点的描述符KeyPoint对象与匹配类DMatch
  9. c调用python接口_通过Python自带C/C++接口实现python与c/c++相互调用
  10. 朋友圈句句刺痛人心的唯美句子有哪些
  11. HTML5初探 - 基本的HTML5模版
  12. 最难的几道Java面试题,看看你跪在第几个?
  13. vlan和静态路由配置实验
  14. Java——(九)IO流
  15. 对Zend5.6加密的php文件进行解密
  16. latex 中表格怎么指定编号_科学网—latex中简单表格制作+标题+编号+固定位置 - 江亿平的博文...
  17. 微信自定义分享功能实现
  18. vivo联合天猫超品日共同打造X70系列城市影像馆
  19. 用mac的chrome浏览器调试 Android 手机的网页
  20. android 极光推送混淆,android 混淆文件的编写(proguard-rules.pro)

热门文章

  1. Oracle的登陆问题和初级学习增删改查(省略安装和卸载)
  2. jquery学习(六)-jquery中的动画
  3. C#中的==、Equal、ReferenceEqual(转载)
  4. 使用liner、feather、multiband对已经拼接的数据进行融合(下)
  5. MySQL中的编码方式错误
  6. CMSGC造成内存碎片的解决方法
  7. 服务不支持 chkconfig 的解决方法
  8. 如何复制静态文件以使用Webpack构建目录?
  9. 学习笔记(2)IPC机制
  10. 广西互联网金融平台系列-7条奇怪的投资数据