文档章节

栈和堆的区别是什么? 为什么说栈的速度快,堆的速度慢?

浮躁的码农
 浮躁的码农
发布于 2016/04/20 09:12
字数 2575
阅读 35
收藏 0
点赞 1
评论 0

为什么说栈的速度快,堆的速度慢?

因为:栈是编译时就将占用内存大小计算好,因此速度快一点。而堆是在运行时才去计算对象所占空间大小,所以栈的速度快 
cpu有专门的寄存器(esp,ebp)来操作栈,堆都是使用间接寻址的。栈快点。 

================================================================================================================= 
栈中的数据占内存大小在编译时是确定的,比如一个int类型就占4B,所以变量地址好计算,所以分配和销毁和访问速度都比较快.  
堆中的数据占内存大小一般在编译时是不确定的,在运行时才能知道大小,所以其地址只有在运行时计算,而且运行时可能占内存大小还有变动,所以对这样的数据的分配,销毁和访问都非常不方便,速度也慢一些.  

====================================================================================================================

栈与堆都是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”。 
比较类里面的数值是否相等时,用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()方法才能保证每次都创建一个新的对象。 
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 
====================================================================================================================
Java虚拟机提供了程序运行时环境,运行时环境中最重要的一个资源是运行时数据区。 
运行时数据区是操作系统为Java虚拟机进程分配的内存区域,并由虚拟机管辖,同时这块区域又分为若干个子区域, 
主要包括堆区、方法区和Java栈区。 
在堆区中存放对象, 
在方法区存放类的类型信息,类型信息包括静态变量和方法信息,方法信息中包含类的所有方法的字节码。 

栈,有个特点,先进去的后出来,里边放的是进程中正在执行的方法, 
比如说方法 


public void A(){ 
    B(); 

public void B(){ 
    C(); 

public void C(){ 


假设一个程序只调用A()方法,(开始时栈是空的): 

程序运行的时候,先执行A()方法,因为A方法调用了B()方法,这时因为A()没有执行完,中断A()方法,将其放入栈中暂存,(此时因为栈原来是空的,A方法被push进栈后,位于栈底),执行B()方法,执行时因为B方法调用了C()方法,此时中断B方法的执行,将B()方法push入栈,由于栈中已经有A方法,B被push进去后,位于A的上面;C()方法执行完后,去栈中查找位于栈顶的方法,查到是方法B,方法B继续执行,执行完后再去栈中查找位于栈顶的方法,此时栈内只又一个方法,那就是方法A,方法A有继续执行,A执行完后,栈中已经没有方法,程序运行结束。 

假设下边这个柱状的东西是栈 
----------------------------------- 
|  | 
|B()|---位于栈口最近的方法先被pop出来执行完成 
|A()|---栈底的方法最后执行完 
——— 

==================================================================================================================
java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。 

基本数据类型存储在“栈”中,对象引用类型实际存储在“堆”中,在栈中只是保留了引用内存的地址值。 
=================================================================================================================
栈中的数据占内存大小在编译时是确定的,比如一个int类型就占4B,所以变量地址好计算,所以分配和销毁和访问速度都比较快. 
堆中的数据占内存大小一般在编译时是不确定的,在运行时才能知道大小,所以其地址只有在运行时计算,而且运行时可能占内存大小还有变动,所以对这样的数据的分配,销毁和访问都非常不方便,速度也慢一些. 
===================================================================================================================
栈里放的是地址,堆里可以放数据也可以放地址(想象下堆里的东西也有可能指向别的地方) 
每个地址都会指向给定的数据,不然就没有存在的必要了,同样的道理,堆中的数据没有被指针指向的话,也没有存在的必要了,所以当obj=null时就释放内存了。 
Java有个好处就是没有指针,Java中的传递的都是传引用,不像c++还能传地址,比如指针p++和p+1两个的结果完全不同。 

© 著作权归作者所有

共有 人打赏支持
浮躁的码农

浮躁的码农

粉丝 57
博文 609
码字总数 141390
作品 0
松江
程序员

暂无相关文章

对于程序员的招聘问题,作为软件人的一些吐槽和建议

作为软件人,找工作有时候似乎挺苦逼的。 说真的,让我去掉前面这句中“似乎”二字吧。就是苦逼!很多人都曾抱怨处在招聘的一方很糟糕——我们没有任何可靠的方式来甄别会写代码并且写得好的...

老道士 ⋅ 33分钟前 ⋅ 0

HDFS原理学习

一、概述 1、 Hadoop整合了众多的文件系统,首先提供了一个高层的文件系统抽象org.apache.hadoop.fs.FileSystem。然后有各个文件系统的实现类。 2、Hadoop是JAVA编写的,不同文件系统之间的交...

cjxcloud ⋅ 37分钟前 ⋅ 0

Linux下MySQL表名不区分大小写的设置方法(抄袭别人的)

Linux下MySQL表名不区分大小写的设置方法 MySQL表名不区分大小写的设置方法 在用centox安装mysql后,把项目的数据库移植了过去,发现一些表的数据查不到,排查了一下问题,最后发现是表名的大...

随风而浮沉 ⋅ 42分钟前 ⋅ 0

ubuntu下安装宋体simsun

sudo cp simsun.ttc /usr/share/fonts cd /usr/share/fonts sudo chmod 644 simsun.ttc 更新字体缓存: 代码: sudo mkfontscale 代码: sudo mkfontdir 代码: sudo fc-cache -fsv 安装chrome扩......

wangxuwei ⋅ 43分钟前 ⋅ 0

利用 ssh 传输文件

Linux 下一般可以用 scp 命令通过 ssh 传送文件: #把服务器上的 /home/user/a.txt 发送到本机的 /var/www/local_dir 目录下scp username@servername:/home/user/a.txt /var/www/local_dir...

大灰狼时间 ⋅ 54分钟前 ⋅ 0

web3j教程:android和java程序员如何使用web3j开发区块链以太坊

如何使用web3j为Java应用或Android App增加以太坊区块链支持,本教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤...

智能合约 ⋅ 今天 ⋅ 0

web3j开发java或android以太坊智能合约快速入门

web3j简介 web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 可以通过它进行以太坊区块链...

笔阁 ⋅ 今天 ⋅ 0

一起读书《深入浅出nodejs》-异步I/O

异步I/O “异步”这个名词其实很早就诞生了,但它大规模流行却是在Web 2.0浪潮中,它伴随着AJAX的第一个A(Asynchronous)席卷了Web。 为什么要异步I/O 关于异步I/O为何在Node里如此重要,这与...

小草先森 ⋅ 今天 ⋅ 0

JVM各种问题

1、如果启动什么都不设,会怎样? 先来看一个命令 [root@localhost bin]# java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=29899008 -XX:MaxHeapSize=478384128 -XX:+PrintCo......

算法之名 ⋅ 今天 ⋅ 0

SAS笔记-宏2

宏是一种文本,一般来说其编译是在程序执行之前。 宏变量的创建 %let语句 %let macro_variables = text; %let是常见的宏变量建立方式,其编译就在执行前。如下例中,想要宏变量test等于数据集...

tonorth123 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部