(转)认识java中的堆和栈
认识堆与栈
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、anewarray和 multianewarray等指令建立,它们不需要程序代码来显式的释放。
堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,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”。
对于String类,比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
ps:这里涉及到equals()方法的源码,String类重写了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的方式是生成不同的对象。每一次生成一个。
因此用第二种方式(String str1 ="abc")来创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。
而对于String str = new String("abc")的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。
另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。
小心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
汇总
一、java中内存分配策略及堆和栈的比较
1、内存分配策略
a.按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.
这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
b.栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.
和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样, 栈式存储分配按照先进后出的原则进行分配。
c.静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,
而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.
2、JVM中的堆和栈总结
Java堆:
1.程序运行中动态分配内存大小
2.java的垃圾回收器自动回收不再使用的数据
Java栈:
1.栈数据可以共享
2.存在栈中的数据大小与生存期必须是确定的。
3.栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象引用。
为什么会有栈内存和堆内存之分?

ps:上面的文字详细回答了创建的对象,存储的位置,生命周期。
(转)认识java中的堆和栈的更多相关文章
- java中的堆、栈、常量池
java中的堆.栈.常量池 分类: java2010-01-15 03:03 4248人阅读 评论(5) 收藏 举报 javastring编译器jvm存储equals Java内存分配: 1. 寄存器 ...
- java中的堆与栈
Java 中的堆和栈 Java把内存划分成两种:一种是栈内存,一种是堆内存. 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配 . 当在一段代码块定义一个变量时,Java就在栈中 ...
- 详细介绍Java中的堆、栈和常量池
下面主要介绍JAVA中的堆.栈和常量池: 1.寄存器 最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈 存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在 ...
- Java中的堆和栈
Java中的堆和栈 栈内存 存放基本数据类型和引用变量 堆内存 存放运行时创建的对象 一般来说,通过new关键字创建出来的对象都放在堆内存中 由于JVM是基于堆栈的虚拟机,而每个Java程序都运行在一 ...
- Java中的堆和栈的区别
当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更糟糕的是,Java中存在栈这样一个后进先 ...
- java中的堆、栈、常量池以及String类型的两种声明
参考自http://blog.sina.com.cn/s/blog_798b04f90100ta67.html http://www.cnblogs.com/fguozhu/articles/2661 ...
- Java基础知识强化03:Java中的堆与栈
1.在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题. 一般,JV ...
- (转)举例讲解JAVA中的堆和栈
转自:http://blog.csdn.net/lifuxiangcaohui/article/details/24936839 Java中堆和堆栈的区别 1. 栈(stack)与堆(heap)都是J ...
- java中的堆,栈和方法区(转)
来源:https://www.cnblogs.com/iliuyuet/p/5603618.html https://blog.csdn.net/lin542405822/article/detail ...
随机推荐
- linux定时任务访问url
这次linux定时任务设置成功,也算是自己学习linux中一个小小的里程碑.:) 撒花撒花--- 以下操作均是在ubuntu 下操作的,亲测有效,其他的linux系统还望亲们自己去查.鞠躬感谢! 1 ...
- ActiveMQ 和消息简介
Apache ActiveMQ 是远程系统间进行通信的消息代理,实现了 JMS(Java Message Service,Java 消息服务).尽管 ActiveMQ 是使用 Java 写的,但是其提 ...
- 新增article注意事项
默认情况下在日志复制中如果新增加Article那么需要产生一个包含所有Article的Snapshot.由于产生Snapshot会加锁,同时会产生IO操作,所以对于大的数据库性能影响是很大的. 可以通 ...
- if 一元二次方程求根
if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码 if...else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码 if...else if... ...
- eclipse debug URLClassPath.getLoader(int) file
版权声明:本文为博主原创文章,未经博主允许不得转载. URLClassPath.getLoader 在用Eclipse调试Java程序时,新手遇到的一个问题是断点老是执行不到,弹出URLClassPa ...
- Redis客户端管理工具的安装及使用
1.下载及安装 请到官网下载:www.treesoft.cn,要最新的版本treeNMS, window系统下载直接解压,就可以用了,免安装,免布署. 2.登录及连接参数配置 登录后,要配置连接参数信 ...
- Linux下SVN+多个Tomcat自动部署
项目中都是jsp开发,所以用到Tomcat. 在我文章中也写过多个Tomcat 的部署,具体可以参考:http://www.cnblogs.com/magmell/p/7045193.html SVN ...
- AngularJS数据双向绑定
<body ng-app> <div ng-controller="myCtrl"> <input ng-model="abc" ...
- tkinter模块常用参数(python3)
1.使用tkinter.Tk() 生成主窗口(root=tkinter.Tk()):root.title('标题名') 修改框体的名字,也可在创建时使用className参数来命名:root.r ...
- Wireshark初步入门
第一次捕获数据包 为了能让Wireshark得到一些数据包,你可以开始你的第一次数据包捕获实验了.你可能会想:"当网络什么问题都没有的时候,怎么能捕获数据包呢?" 首先,网络总是有 ...