志在指尖
用双手敲打未来

java堆栈(java堆栈和栈的区别)

java堆栈

JAVA在程序运行时,在内存中划分5片空间进行数据的存储。分别是:1:寄存器。2:本地办法区。3:办法区。4:栈。5:堆。
基本,栈stack和堆heap这两个概念很重要,不了解清楚,后边就不必学了。
以下是这几天栈和堆的学习记录和心得。得些记录下来。今后有学到新的,会慢慢弥补。java
一、先说一下最基本的要点
基本数据类型、局部变量都是存放在栈内存中的,用完就消失。
new创立的实例化对象及数组,是存放在堆内存中的,用完之后靠垃圾回收机制不定期自动消除。
栈:
函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
栈内存中的数据,没有默认初始化值,需要手动设置。
堆:
堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一(C++中需要程序员手动清除)。

java堆栈和栈的区别

在正式内容开始之前要说明一点,咱们常常所说的仓库仓库是堆和栈统称,堆是堆,栈是栈,合在一起统称仓库;
1.栈(stack)与堆(heap)都是Java用来在Ram中寄存数据的地方。与C++不同,Java主动管理栈和堆,程序员不能直接地设置栈或堆。
2.栈的优势是,存取速度比堆要快,仅次于直接坐落CPU中的寄存器。但缺陷是,存在栈中的数据巨细与生存期必须是确定的,缺乏灵活性。别的,栈数据能够共享,详见第3点。堆的优势是能够动态地分配内存巨细,生存期也不必事先告诉编译器,Java的废物收集器会主动收走这些不再运用的数据。但缺陷是,由于要在运行时动态分配内存,存取速度较慢。
3.Java中的数据类型有两种。
一种是根本类型(primitivetypes),共有8种,即int,short,long,byte,float,double,boolean,char(留意,并没有string的根本类型)。这种类型的界说是经过比如inta=3;longb=255L;的方法来界说的,称为主动变量。值得留意的是,主动变量存的是字面值,不是类的实例,即不是类的引证,这儿并没有类的存在。如inta=3;这儿的a是一个指向int类型的引证,指向3这个字面值。这些字面值的数据,由于巨细可知,生存期可知(这些字面值固定界说在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。java堆栈
别的,栈有一个很重要的特别性,就是存在栈中的数据能够同享。假设咱们一起界说:
1inta=3;
2intb=3;
编译器先处理inta=3;首要它会在栈中创立一个变量为a的引证,然后查找有没有字面值为3的地址,没找到,就开辟一个寄存3这个字面值的地址,然后将a指向3的地址。接着处理intb=3;在创立完b的引证变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b一起均指向3的状况。
特别留意的是,这种字面值的引证与类目标的引证不同。假定两个类目标的引证一起指向一个目标,假如一个目标引证变量修正了这个目标的内部状况,那么另一个目标引证变量也立刻反映出这个改动。相反,经过字面值的引证来修正其值,不会导致另一个指向此字面值的引证的值也跟着改动的状况。如上例,咱们界说完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会从头搜索栈中是否有4的字面值,假如没有,从头开辟地址寄存4的值;假如已经有了,则直接将a指向这个地址。因而a值的改动不会影响到b的值。
另一种是包装类数据,如Integer,String,Double等将相应的根本数据类型包装起来的类。这些类数据悉数存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才依据需要动态创建,因而比较灵活,但缺陷是要占用更多的时刻。
4.String是一个特别的包装类数据。即能够用Stringstr=newString(“abc”);的方法来创立,也能够用Stringstr=”abc”;的方法来创立(作为比照,在JDK5.0之前,你从未见过Integeri=3;的表达式,由于类与字面值是不能通用的,除了String。而在JDK5.0中,这种表达式是能够的!由于编译器在后台进行Integeri=newInteger(3)的转化)。前者是规范的类的创立进程,即在Java中,一切都是目标,而目标是类的实例,悉数经过new()的方法来创立。Java中的有些类,如DateFormat类,能够经过该类的getInstance()方法来回来一个新创立的类,好像违反了此准则。其实不然。该类运用了单例形式来回来类的实例,只不过这个实例是在该类内部经过new()来创立的,而getInstance()向外部隐藏了此细节。那为什么在Stringstr=”abc”;中,并没有经过new()来创立实例,是不是违反了上述准则?其实没有。
5.关于Stringstr=”abc”的内部工作。Java内部将此语句转化为以下几个步骤:
(1)先界说一个名为str的对String类的目标引证变量:Stringstr;
(2)在栈中查找有没有寄存值为”abc”的地址,假如没有,则开辟一个寄存字面值为”abc”的地址,接着创立一个新的String类的目标o,并将o的字符串值指向这个地址,并且在栈中这个地址周围记下这个引证的目标o。假如已经有了值为”abc”的地址,则查找目标o,并回来o的地址。
(3)将str指向目标o的地址。
值得留意的是,一般String类中字符串值都是直接存值的。但像Stringstr=”abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引证!
为了更好地说明这个问题,咱们能够经过以下的几个代码进行验证。
1Stringstr1=”abc”;
2Stringstr2=”abc”;
3System.out.println(str1==str2);//true
留意,咱们这儿并不必str1.equals(str2);的方法,由于这将比较两个字符串的值是否持平。==号,依据JDK的说明,只要在两个引证都指向了同一个目标时才回来真值。而咱们在这儿要看的是,str1与str2是否都指向了同一个目标。
结果说明,JVM创立了两个引证str1和str2,但只创立了一个目标,并且两个引证都指向了这个目标。
咱们再来更进一步,将以上代码改成:
1Stringstr1=”abc”;
2Stringstr2=”abc”;
3str1=”bcd”;
4System.out.println(str1+”,”+str2);//bcd,abc
5System.out.println(str1==str2);//false
这就是说,赋值的改动导致了类目标引证的改动,str1指向了别的一个新目标!而str2仍旧指向本来的目标。上例中,当咱们将str1的值改为”bcd”时,JVM发现在栈中没有寄存该值的地址,便开辟了这个地址,并创立了一个新的目标,其字符串的值指向这个地址。
事实上,String类被设计成为不行改动(immutable)的类。假如你要改动其值,能够,但JVM在运行时依据新值悄然创立了一个新目标,然后将这个目标的地址回来给本来类的引证。这个创立进程虽说是完全主动进行的,但它究竟占用了更多的时刻。在对时刻要求比较敏感的环境中,会带有必定的不良影响。
再修正本来代码:
Stringstr1=”abc”;
Stringstr2=”abc”;
str1=”bcd”;
Stringstr3=str1;
System.out.println(str3);//bcd
Stringstr4=”bcd”;
System.out.println(str1==str4);//true
str3这个目标的引证直接指向str1所指向的目标(留意,str3并没有创立新目标)。当str1改完其值后,再创立一个String的引证str4,并指向因str1修正值而创立的新的目标。能够发现,这回str4也没有创立新的目标,从而再次完成栈中数据的同享。
咱们再接着看以下的代码。
1Stringstr1=newString(“abc”);
2Stringstr2=”abc”;
3System.out.println(str1==str2);//false
创立了两个引证。创立了两个目标。两个引证别离指向不同的两个目标。
1Stringstr1=”abc”;
2Stringstr2=newString(“abc”);
3System.out.println(str1==str2);//false
创立了两个引证。创立了两个目标。两个引证别离指向不同的两个目标。
以上两段代码说明,只要是用new()来新建目标的,都会在堆中创立,并且其字符串是独自存值的,即便与栈中的数据相同,也不会与栈中的数据同享。
6.数据类型包装类的值不行修正。不仅仅是String类的值不行修正,一切的数据类型包装类都不能更改其内部的值。
7.定论与主张:
(1)咱们在运用比如Stringstr=”abc”;的格局界说类时,总是想当然地以为,咱们创立了String类的目标str。担心圈套!目标或许并没有被创立!仅有能够必定的是,指向String类的引证被创立了。至于这个引证究竟是否指向了一个新的目标,必须依据上下文来考虑,除非你经过new()方法来显要地创立一个新的目标。因此,更为精确的说法是,咱们创立了一个指向String类的目标的引证变量str,这个目标引证变量指向了某个值为”abc”的String类。清醒地认识到这一点对扫除程序中难以发现的bug是很有协助的。
(2)运用Stringstr=”abc”;的方法,能够在必定程度上进步程序的运行速度,由于JVM会主动依据栈中数据的实践状况来决定是否有必要创立新目标。而关于Stringstr=newString(“abc”);的代码,则一概在堆中创立新目标,而不论其字符串值是否持平,是否有必要创立新目标,从而加剧了程序的担负。这个思维应该是享元形式的思维,但JDK的内部在这儿完成是否应用了这个形式,不得而知。
(3)当比较包装类里面的数值是否持平时,用equals()方法;当测试两个包装类的引证是否指向同一个目标时,用==。
(4)由于String类的immutable性质,当String变量需要常常改换其值时,应该考虑运用StringBuffer类,以进步程序功率。
其实在实践应用中,栈多用来存储方法的调用。而堆则用于目标的存储。
JAVA中的根本类型,其实需要特别对待。由于,在JAVA中,经过new创立的目标存储在“堆”中,所以用new创立一个小的、简单的变量,如根本类型等,往往不是很有效。因而,在JAVA中,关于这些类型,采用了与C、C++相同的方法。也就是说,不必new来创立,而是创立一个并非是“引证”的“主动”变量。这个变量具有它的“值”,并置于栈中,因而更高效。

未经允许不得转载:IT技术网站 » java堆栈(java堆栈和栈的区别)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载