java的堆栈

java的堆栈
java的堆栈

Java堆栈

jvm为每个新创建的线程都分配一个堆栈。堆栈以帧为单位保存

线程的状态。jvm对堆栈只进行两种操作:以帧为单位的压栈和出栈

操作。

某个线程正在执行的方法称为此线程的当前方法。当前方法使用的帧称

为当前帧。当前方法所属的类称为当前类。当前类的常量池称为当前

常量池。当线程执行一个方法时,它会跟踪当前的类和常量池。当jvm

会在当前帧内执行帧内数据的操作。

当线程激活一个java方法,jvm就会在线程的java堆栈里新压入一个帧。

这个帧自然成为了当前帧。在此方法执行期间,这个帧将用来保存参数,

局部变量,中间计算过程和其他数据。

一个方法可以以两种方法结束。一种是正常返回结束。一种是通过

异常抛出而异常结束(abrupt completion)。不管以那种方式返回,jvm

都会将当前帧弹出堆栈然后释放掉,这样上一个方法的帧就成为当前帧了。

(译者:可能可以这样理解,位于堆栈顶部的帧为当前帧)

java堆栈上的所有数据都为此线程私有。一个线程不能访问另一个线程

的堆栈数据,所以在多线程的情况下也不需要对堆栈数据的访问进行同步。

象方法区和堆一样(见以前的译文),java堆栈和帧在内存中也不必是连续

的。帧可以分布在连续的内存区,也可以不是。帧的数据结构由jvm的实现者

来决定,他们可以允许用户指定java堆栈的初始大小或最大最小尺寸。

堆栈帧( The Stack Frame)

堆栈帧有三部分:局部变量区,操作数堆栈和帧数据区。局部变量区和操作数堆栈的大小要视对应的方法而定。编译器在编译的时候就对每个方法进行了计算并放在了类文件(class file)中了。帧数据区的大小对一种jvm实现来说是一定的。

当jvm激活一个方法时,它从类信息数据得到此方法的局部变量区和操作数堆栈的大小,并据此分配大小合适堆栈帧压入java堆栈中。

局部变量区

java堆栈帧的局部变量区是一个基为零类型为word的数组。指令通过索引来

使用这些数据。类型为int,float,reference和returnAddress的值在

数组中占据一项,类型为byte,short,和char的值在存入数组前都转为了

int值而占据一项。类型为long和double的值在数组中占据连续的两项,在

访问他们的时候,指令提供第一项的索引。例如一个long值占据3,4项,指令会

取索引为3的long值。局部变量区的所有值都是字对齐的,long和doubles 的起始索引值没有限定。

局部变量区包含此方法的参数和局部变量。编译器首先以声明的顺序把参数放入局部数据区。图5-9显示了下面两个方法的变量区。

// On CD-ROM in file jvm/ex3/Example3a.java

class Example3a {

public static int runClassMethod(int i, long l, float f,

double d, Object o, byte b) {

return 0;

}

public int runInstanceMethod(char c, double d, short s,

boolean b) {

return 0;

}

}

图5-9. 局部变量区中的方法参数

注意在方法runInstanceMethod()的帧中,第一个参数是一个

类型为reference的值,尽管方法没有显示的声明这个参数,但

这是个对每个实例方法(instance method)都隐含加入的一个

参数值,用来代表调用的对象。(译者:与c++中的this指针一样)

我们看方法runClassMethod()就没有这个变量,这是因为这是一

个类方法(class method),类方法与类相关,而不与对象相关。

我们注意到在源码中的byte,short,char和boolean在局部变量区

都成了ints。在操作数堆栈也是同样的情况。如前所述,jvm不直接

支持boolean类型,java编译器总是用ints来表示boolean。但java

对byte,short和char是支持的,这些类型的值可以作为实例变量

存储在局部变量区中,也可以作为类变量存储在方法区中。但在局部变量区和操作数堆栈中都被转成了ints类型的值,期间的运算也是以int来的,

只当存回堆或方法区中,才会转回原来的类型。

同样需要注意的是runClassMethod()的对象o。在java中,所以的对象

都以引用(reference)传递。所有的对象都存储在堆中,你永远都不会在

局部变量区或操作数堆栈中发现对象的拷贝,只会有对象引用。

编译器对局部变量的放置方法可以多种多样,它可以任意决定放置顺序,

甚至可以用一个索引指代两个局部变量。例如,当两个局部变量的作用域

不重叠时,如Example3b的局部变量i和j。

// On CD-ROM in file jvm/ex3/Example3b.java

class Example3b {

public static void runtwoLoops() {

for (int i = 0; i < 10; ++i) {

System.out.println(i);

}

for (int j = 9; j >= 0; --j) {

System.out.println(j);

}

}

}

jvm的实现者对局部变量区的设计仍然有象其他数据区一样的灵活性。

关于long和double数据如何分布在数组中,jvm规范没有指定。

假如一个jvm实现的字长为64位,可以把long或double数据放在

数组中的低项内,而使高项为空。(在字长为32位的时候,需要两项

才能放下一个long或double)。

操作数堆栈

操作数堆栈象局部变量区一样是用一个类型为word的数组存储数据,

但它不是通过索引来访问的,而是以堆栈的方式压入和弹出。假如

一个指令压入了一个值,另一个指令就可以弹出这个值并使用之。

jvm在操作数堆栈中的处理数据类型的方式和局部变量区是一样的,同样

有数据类型的转换。jvm没有寄存器,jvm是基于堆栈的而不是基于寄存器的,因为jvm的指令从堆栈中获得操作数,而不是寄存器。虽然操作数还可以从另外一些地方获得,如字节码中,或常量池内,但主要是从堆栈获得的。

jvm把操作数堆栈当作一个工作区使用。许多指令从此堆栈中弹出数据,进行运算,然后压入结果。例如,iadd指令从堆栈中弹出两个数,相加,然后压入结果。下面显示了jvm是如何进行这项操作的:

iload_0 // push the int in local variable 0

iload_1 // push the int in local variable 1

iadd // pop two ints, add them, push result

istore_2 // pop int, store into local variable 2

在这个字节码的序列里,前两个指令iload_0和iload_1将存储在

局部变量区中索引为0和1的整数压入操作数据区中,然后相加,将

结果压入操作数据区中。第四条指令istore_2从操作数据区中弹出

结果并存储到局部数据区索引为2的地方。在图5-10中,详细的表述

了这个过程,图中,没有使用的区域以空白表示。

图5-10. 两个局部变量的相加.

帧数据区

除了局部变量区和操作数据堆栈外,java栈帧还需要数据来支持

常量池解析(constant pool resolution),方法的正常返回

(normal method return)和异常分派(exception dispatch)。

这些信息保存在帧数据区中。

jvm中的许多指令都涉及到常量池的数据。一些指令仅仅是取出常量池

中的数据并压入操作数堆栈中。一些指令使用常量池中的数据来指示

需要实例化的类或数组,需要访问的域,或需要激活的方法。还有一些

指令来判断某个对象是否是常量池指定的某个类或接口的子孙实例。

每当jvm要执行需要常量区数据的指令,它都会通过帧数据区中指向

常量区的指针来访问常量区。以前讲过,常量区中对类型,域和方法

的引用在开始时都是符号。如果当指令执行的时候仍然是符号,jvm

就会进行解析。

除了常量区解析外,帧数据区还要帮助jvm处理方法的正常和异常结束。

正常结束,jvm必须恢复方法调用者的环境,包括恢复pc指针。假如

方法有返回值,jvm必须将值压入调用者的操作数堆栈。

为了处理方法的异常退出,帧数据区必须保存对此方法异常表的引用。

一个异常表定义了这个方法受catch子句保护的区域,每项都有一个

catch子句的起始和开始位置(position),和用来表示异常类在常量池

中的索引,以及catch子句代码的起始位置。

当一个方法抛出异常时,jvm使用帧数组区指定的异常表来决定如何处理。

如果找到了匹配的catch子句,就会转交控制权。如果没有发现,方法会

立即结束。jvm使用帧数据区的信息恢复调用者的帧,然后重新抛出同样

的异常。

除了上述信息外,jvm的实现者也可以将其他信息放入帧数据区,如调试

数据。

java堆栈的一种实现

实现者可以按自己的想法设计java堆栈。如以前所讲,一个方法是从堆中

单独的分配帧。我以此为例,看下面的类:

// On CD-ROM in file jvm/ex3/Example3c.java

class Example3c {

public static void addAndPrint() {

double result = addTwoTypes(1, 88.88);

System.out.println(result);

}

public static double addTwoTypes(int i, double d) {

return i + d;

}

}

图5-11显示了一个线程执行这个方法的三个快照。在这个jvm的实现中,

每个帧都单独的从堆中分配。为了激活方法addTwoTypes(),方法addAndPrint()首先压入int 1和double88.88到操作数堆栈中,然后

激活addTwoTypes()方法。

图5-11. 帧的分配

激活addTwoTypes()的指令使用了常量池的数据,jvm在常量池中查找这些数据如果有必要则解析之。

注意addAndPrint()方法使用常量池引用方法addTwoTypes(),尽管

这两个方法是属于一个类的。象引用其他类一样,对同一个类的方法和域

的引用在初始的时候也是符号,在使用之前需要解析。

解析后的常量池数据项将指向存储在方法区中有关方法addTwoTypes()的信息。jvm将使用这些信息决定方法addTwoTypes()局部变量区和操作数堆栈的大小。如果使用Sun的javac编译器(JDK1.1)的话,方法addTwoTypes()的局部变量区需要三个words,操作数堆栈需要四个words。(帧数据区的大小对某个jvm实现来说是定的)jvm为这个方法分配了足够大小的一个堆栈帧。然后从方法addAndPrint()的操作数堆栈中弹出double参数和int参数(88.88和1)并把他们

分别放在了方法addTwoType()的局部变量区索引为1和0的地方。

当addTwoTypes()返回时,它首先把类型为double的返回值(这里是89.88)

压入自己的操作数堆栈里。jvm使用帧数据区中的信息找到调用者(为addAndPrint())的堆栈帧,然后将返回值压入addAndPrint()的操作数堆栈

中并释放方法addTwoType()的堆栈帧。然后jvm使addTwoType()的堆栈帧

为当前帧并继续执行方法addAndPrint()。

图5-12显示了相同的方法在不同的jvm实现里的执行情况。这里的堆栈帧是在一个连续的空间里的。这种方法允许相邻方法的堆栈帧可以重叠。这里调用者的操作数堆栈就成了被调者的局部变量区。

图5-12. 从一个连续的堆栈中分配帧

这种方法不仅节省了空间,而且节省了时间,因为jvm不必把参数从一个

堆栈帧拷贝到另一个堆栈帧中了。

注意当前帧的操作数堆栈总是在java堆栈的顶部。尽管这样可能

可以更好的说明图5-12的实现。但不管java堆栈是如何实现的,

对操作数堆栈的操作总是在当前帧执行的。这样,在当前帧的

操作数堆栈压入一个数也就是在java堆栈压入一个值。

java堆栈还有一些其他的实现,基本上是上述两种的结合。一个jvm可以

在线程初期时从堆栈分出一段空间。在这段连续的空间里,jvm可以采用

5-12的重叠方法。但在与其他段空间的结合上,就要使用如图5-11的方法

JAVA经验谈:尽可能使用堆栈变量

来源: CSDN 作者: chensheng913

如果您频繁存取变量,就需要考虑从何处存取这些变量。变量是 static 变量,还是堆栈变量,或者是类的实例变量?变量的存储位置对存取它的代码的性能有明显的影响?例如,请考虑下面这段代码:

class StackVars

{

private int instVar;

private static int staticVar;

//存取堆栈变量

void stackAccess(int val)

{

int j=0;

for (int i=0; i

j += 1;

}

//存取类的实例变量

void instanceAccess(int val)

{

for (int i=0; i

instVar += 1;

}

//存取类的 static 变量

void staticAccess(int val)

{

for (int i=0; i

staticVar += 1;

}

}

这段代码中的每个方法都执行相同的循环,并反复相同的次数。唯一的不同是每个循环使一个不同类型的变量递增。方法 stackAccess 使一个局部堆栈变量递增,instanceAccess 使类的一个实例变量递增,而 staticAccess 使类的一个 static 变量递增。

instanceAccess 和 staticAccess 的执行时间基本相同。但是,stackAccess 要快两到三倍。存取堆栈变量如此快是因为,JVM 存取堆栈变量比它存取 static 变量或类的实例变量执行的操作少。请看一下为这三个方法生成的字节码:

Method void stackAccess(int)

0 iconst_0 //将 0 压入堆栈。

1 istore_

2 //弹出 0 并将它存储在局部分变量表中索引为 2 的位置(j)。

2 iconst_0 //压入 0。

3 istore_3 //弹出 0 并将它存储在局部变量表中索引为 3 的位置

(i)。

4 goto 13 //跳至位置 13。

7 iinc 2 1 //将存储在索引 2 处的 j 加 1。

10 iinc 3 1 //将存储在索引 3 处的 i 加 1。

13 iload_3 //压入索引 3 处的值 (i)。

14 iload_1 //压入索引 1 处的值 (val)。

15 if_icmplt 7 //弹出 i 和 val。如果 i 小于 val,则跳至位置 7。

18 return //返回调用方法。

Method void instanceAccess(int)

0 iconst_0 //将 0 压入堆栈。

1 istore_

2 //弹出 0 并将它存储在局部变量表中索引为 2 的位置

(i)。

2 goto 18 //跳至位置 18。

5 aload_0 //压入索引 0 (this)。

6 dup //复制堆栈顶的值并将它压入。

7 getfield #19

//弹出 this 对象引用并压入 instVar 的值。

10 iconst_1 //压入 1。

11 iadd //弹出栈顶的两个值,并压入它们的和。

12 putfield #19

//弹出栈顶的两个值并将和存储在 instVar 中。

15 iinc 2 1 //将存储在索引 2 处的 i 加 1。

18 iload_2 //压入索引 2 处的值 (i)。

19 iload_1 //压入索引 1 处的值 (val)。

20 if_icmplt 5 //弹出 i 和 val。如果 i 小于 val,则跳至位置 5。

23 return //返回调用方法。

Method void staticAccess(int)

0 iconst_0 //将 0 压入堆栈。

1 istore_

2 //弹出 0 并将它存储在局部变量表中索引为 2 的位置

(i)。

2 goto 16 //跳至位置 16。

5 getstatic #25

//将常数存储池中 staticVar 的值压入堆栈。

8 iconst_1 //压入 1。

9 iadd //弹出栈顶的两个值,并压入它们的和。

10 putstatic #25

//弹出和的值并将它存储在 staticVar 中。

13 iinc 2 1 //将存储在索引 2 处的 i 加 1。

16 iload_2 //压入索引 2 处的值 (i)。

17 iload_1 //压入索引 1 处的值 (val)。

18 if_icmplt 5 //弹出 i 和 val。如果 i 小于 val,则跳至位置 5。

21 return //返回调用方法。

查看字节码揭示了堆栈变量效率更高的原因。JVM 是一种基于堆栈的虚拟机,因此优化了对堆栈数据的存取和处理。所有局部变量都存储在一个局部变量表中,在Java 操作数堆栈中进行处理,并可被高效地存取。存取 static 变量和实例变量成本更高,因为 JVM 必须使用代价更高的操作码,并从常数存储池中存取它们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用。)

通常,在第一次从常数存储池中访问 static 变量或实例变量以后,JVM 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。

考虑到这些事实,就可以重新构建前面的代码,以便通过存取堆栈变量而不是实例变量或 static 变量使操作更高效。请考虑修改后的代码:

class StackVars

{

//与前面相同...

void instanceAccess(int val)

{

int j = instVar;

for (int i=0; i

j += 1;

instVar = j;

}

void staticAccess(int val)

{

int j = staticVar;

for (int i=0; i

j += 1;

staticVar = j;

}

}

方法 instanceAccess 和 staticAccess 被修改为将它们的实例变量或 static 变量复制到局部堆栈变量中。当变量的处理完成以后,其值又被复制回实例变量或static 变量中。这种简单的更改明显提高了 instanceAccess 和 staticAccess 的性能。这三个方法的执行时间现在基本相同,instanceAccess 和 staticAccess 的执行速度只比 stackAccess 的执行速度慢大约 4%。

这并不表示您应该避免使用 static 变量或实例变量。您应该使用对您的设计有意义的存储机制。例如,如果您在一个循环中存取 static 变量或实例变量,则您可以临时将它们存储在一个局部堆栈变量中,这样就可以明显地提高代码的性能。这将提供最高效的字节码指令序列供 JVM 执行。

这两种方式是不相同的。声明变量/数组的方式,是在栈中分配内存。程序中的每一个函数都有自己的栈,用于为函数作用域内的变量/对象分配存储空间。当调用完此函数返回的时候,栈空间自动被收回,其中的内容也就全部无效了。而new是在堆中分配内存的,而且一经分配则永久保留,直到显式的释放掉。否则会造成内存泄露。实际上Java是通过实现一个“垃圾收集器”在运行期间实现这一点的。
说白了,栈和堆都是进程空间内的一段虚存(将被映射到物理内存,所以也可说是内存中的一段空间),所不同的是,计算机对栈给予了更多支持。还记得栈指针寄存器吗,当程序调用内存时,栈指针将指向栈顶,存贮空间的分配就是通过移动栈指针来完成的。那么,哪些变量将在栈中分配空间呢?既然栈用

起来这么方便,那么要  堆  来干什么呢?显然,如果我们在程序运行之前(编译时)就能知道要申请多少空间,栈就足够了,但在复杂的应用中,人们很难提前知道自己要申请多少空间.我们可以把  堆  看成存储空间的仓库,当我们需要存储空间时就从仓库中领取,不使用这段空间时就把它还回去(这是一个相当复杂的算法)。领取和归还的过程是程序执行时决定的(所谓的动态决定),编译时无法确定存储空间的位置。
线程是程序执行的最小单位,每个线程都有自己独立的堆栈.

java基础总结

第一章初识java 一、java语言的历史 ●第一代java语言:Oak 二、java语言的现状 ?Java SE:主要用于桌面程序的开发。 ?Java EE:主要用于网页程序的开发。 ?Java ME:主要用于嵌入式系统程序的开发。(安卓)三、java语言的特点 ●跨平台(不同的操作系统都可运行) ●简单(没有直接使用指针) ●面向对象(世间万物皆为对象) ●半编译半解释(java文件---class文件----虚拟机) ●分布式(多个客户端访问、通过服务器的配置分发到 不同的服务器) ●健壮(异常的处理) ●安全(任何语言都具备、虚拟机沙箱原理) ●多线程、高性能、动态 四、java语言与C、C++语言的不同与区别 ●自动内存管理:Java对于内存的分配是动态的,并具 有垃圾回收机制。 ●不在类外定义全局变量。 ●Java中将不再使用goto语句。

●Java中取消了指针。 ●运行时系统对类型转换进行类型相容性检查 ●Java不支持头文件,使用import与其它类通讯。 ●Java中不包含结构和联合;所有的内容都封装在类中。 ●Java中不支持宏,它通过final 关键字来声明一个常 量。 ●Java不支持多重继承,可以通过Java中的接口实现 多重继承的功能。 ●CC++ 一般情况下都是偏硬件的,java一般偏软件(应 用、基于浏览器) ●(补充).net、php (网页制作比较快捷)、在安全级 别要求高的企业一般使用java(银行、政府系统) 五、环境的搭建 1、默认路径如下 ●C:\Program Files\Java\jdk1.6.0_02:提供编程中需要 的api包 ●C:\Program Files\Java\jre1.6.0_02:虚拟机文件所在的 位置 2.安装后各个文件夹代表的含义

java中堆和栈的区别

Java中堆与栈的区别 简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存。 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java 自动管理栈和堆,程序员不能直接地设置栈或堆。 2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 3. Java中的数据类型有两种。 一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出

Java中equals和==的区别

Java中equals和==的区别 1、java中equals和==的区别值类型是存储在内存中的堆栈(简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中。 2、==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。 3、equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同。 4、==比较的是2个对象的地址,而equals比较的是2个对象的内容,显然,当equals为true时,==不一定为true。 ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 除了String和封装器,equals()和“==”没什么区别 但String和封装器重写了equals(),所以在这里面,equals()指比较字符串或封装对象对应的原始值是否相等,"=="是比较两个对象是否为同一个对象

==是判断两个对象是否是同一个对象 equals是进行值的判断 String a = new String("aaa"); String b = new String("a"); b += "aa"; 则 a==b //错误 a.equals(b)//正确 equals 方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等。 ==用于比较引用和比较基本数据类型时具有不同的功能:比较基本数据类型,如果两个值相同,则结果为true 而在比较引用时,如果引用指向内存中的同一对象,结果为true Eg:s1 = new String("sony"); //创建的是字符串对象 s1.equals("sony"); //返回 trues1 == "sony" //返回false //如果 s1 = "sony"; s1 == "sony" //返回true

JVM内存分配(栈堆)与JVM回收机制

Java 中的堆和栈 简单的说: Java把内存划分成两种:一种是栈内存,一种是堆内存。 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由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”。 比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。 String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); //true

java复习

1.在Java中,负责对字节代码解释执行的是 A. 应用服务器 B. 虚拟机 C. 垃圾回收器 D. 编译器 2.定义字符串:String s1="hello";对下面程序描述正确的是:if(s1=="hello"){ System.out.println("s1 = hello"); }else{ System.out.println("s1 !=hello"); } A. 输出s1 !=hello B. 编译正确,运行错误 C. 产生编译错误 D. 输出s1=hello 3. 你怎样强制对一个对象立即进行垃圾收集? A. 调用System.gc() B. 调用System.gc(), 同时传递要进行垃圾收集对象的引用 C. 给这个对象的所有引用设置一个新的值(例如null) D. 垃圾收集是不能被强迫立即执行 4. 已知如下代码 public class staTest1 { static int a=10; static{a=a+5;} public static void main(String[] args) { System.out.println("a=:"+a); } static {a=a/3;} } 请问哪个情况是正确的? A、4行与9行不能通过编译,因为缺少方法名和返回类型 B、9行不能通过编译,因为只能有一个静态初始化器 C、编译通过,执行结果为:x=5 D、编译通过,执行结果为:x=15 5.已知如下代码: public class Test { long a[] = new long[10]; public static void main ( String arg[] ) { System.out.println ( a[6] ); }

jvm内存结构

结构概览 JVM内存区域也称为Java运行时数据区域。其中包括:程序计数器、栈、堆、方法区等。 内存结构主要分为三大部分:堆内存,方法区和栈。 堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间。 方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。 JVM和系统调用之间的关系:

Java堆(Heap) Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。 根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。 简要归纳:新的对象分配是首先放在年轻代 (Young Generation) 的Eden区,Survivor区作为 Eden区和Old区的缓冲,在Survivor区的对象经历若干次收集仍然存活的,就会被转移到老年代Old中。 方法区(Method Area) 是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(存放的是Class)。它有一个别名,叫非堆。

Java中的堆和栈

Heap与stack的研究 Think in java第四章的内容是关于内存分配和初始化的,对这一章的学习带出了我以往学习中的一个模糊点:究竟什么是堆存储(Heap)?什么是栈存储(Stack)?有什么区别呢?翻了不少资料,补了这一课,觉得非常受用. 2.1 内存分配策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求. 栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。 静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放. 2.2 堆和栈的比较 上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈: 从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的: 在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快,当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时. 堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译

Java中堆与栈的内存分配共5页word资料

Java中堆与栈的内存分配 文献标识码:A 1 栈内存与堆内存概述 在函数中定义的基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码中定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。 堆内存用来存放由new创建的对象和数组,在堆中分配的内存,由Java 虚拟机的自动垃圾回收器来管理。在堆中产生一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器释放掉。 2 Java中内存分配策略 程序运行时,内存的分配有三种策略,分别是静态的、栈式的和堆式的。 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存

储空间需求,因而在编译时就可以给其分配固定的内存空间。这种分配策略要求程序代码中不允许有可变数据结构的存在,也不允许有嵌套或者递归的结构出现,因为会导致编译程序无法计算准确的存储空间需求。 栈式存储分配也称为动态存储分配,是由一个类似于堆栈的运行栈来实现的,和静态存储分配相反。在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道。但是规定在运行中进入一个程序模块时,必须知道程序模块所需的数据区大小才能够为其分配内存。栈式存储分配按照先进后出的原则进行分配。 堆式存储分配专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例。堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放。而静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求。 3 堆和栈的比较 从堆和栈的功能和作用来看,堆主要用来存放对象的,栈主要是用来执行程序的。在程序设计中,所有的方法调用都是通过栈进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。退出函数的时候,修改栈指针就可以把栈中的内容销毁。该模式速度最快,而且在编译的时候就已经为一个即将要调用的程序模块设置好数据区的大小,运行时再分配下去。 堆是应用程序在运行的时候请求操作系统分配给自己内存,由于是操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低。但是堆的优点在于,编译器不必知道要从堆里分配多少存储

数据结构(Java)复习题及答案 3栈和队列

第4章栈和队列 一、填空题 1.向量、栈和队列都是线性结构,可以在向量的任何位置插入和删除元素;对于栈只能在栈顶插入和删除元素;对于队列只能在队尾插入和队首删除元素。 2. 栈是一种特殊的线性表,允许插入和删除运算的一端称为栈顶。不允许插入和删除运算的一端称为栈底。 3. 队列是被限定为只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表。 4. 在一个循环队列中,队首指针指向队首元素的前一个位置。 5. 在具有n个单元的循环队列中,队满时共有n-1个元素。 6. 向栈中压入元素的操作是先移动栈顶指针,后存入元素。 7. 从循环队列中删除一个元素时,其操作是先移动队首指针,后取出元素。 二、判断正误(判断下列概念的正确性,并作出简要的说明。) (×)1. 线性表的每个结点只能是一个简单类型,而链表的每个结点可以是一个复杂类型。 错,线性表是逻辑结构概念,可以顺序存储或链式存储,与元素数据类型无关。(×)2. 在表结构中最常用的是线性表,栈和队列不太常用。 错,不一定吧?调用子程序或函数常用,CPU中也用队列。 (√)3. 栈是一种对所有插入、删除操作限于在表的一端进行的线性表,是一种后进先出型结构。 (√)4. 对于不同的使用者,一个表结构既可以是栈,也可以是队列,也可以是线性表。 正确,都是线性逻辑结构,栈和队列其实是特殊的线性表,对运算的定义略有不同而已。 (×)5. 栈和链表是两种不同的数据结构。 错,栈是逻辑结构的概念,是特殊殊线性表,而链表是存储结构概念,二者不是同类项。 (×)6. 栈和队列是一种非线性数据结构。

错,他们都是线性逻辑结构,栈和队列其实是特殊的线性表,对运算的定义略有不同而已。 (√)7. 栈和队列的存储方式既可是顺序方式,也可是链接方式。 (√)8*. 两个栈共享一片连续内存空间时,为提高内存利用率,减少溢出机会,应把两个栈的栈底分别设在这片内存空间的两端。 (×)9. 队是一种插入与删除操作分别在表的两端进行的线性表,是一种先进后出型结构。 错,后半句不对。 (×)10. 一个栈的输入序列是12345,则栈的输出序列不可能是12345。 错,有可能。 三、单项选择题 (B)1.栈中元素的进出原则是 A.先进先出B.后进先出C.栈空则进D.栈满则出 (B)2. 判定一个栈ST(最多元素为m0)为空的条件是 A.ST→top<>0 B.ST→top=-1 C.ST→top<>m0 D.ST→top=m0 四、简答题 1. 说明线性表、栈与队的异同点。 答:相同点:都是线性结构,都是逻辑结构的概念。都可以用顺序存储或链表存储;栈和队列是两种特殊的线性表,即受限的线性表,只是对插入、删除运算加以限制。 不同点:①运算规则不同,线性表为随机存取,而栈是只允许在一端进行插入、删除运算,因而是后进先出表LIFO;队列是只允许在一端进行插入、另一端进行删除运算,因而是先进先出表FIFO。 ②用途不同,堆栈用于子程调用和保护现场,队列用于多道作业处理、指令寄存及其他运算等等。 2*.设有编号为1,2,3,4的四辆列车,顺序进入一个栈式结构的车站,具体写出这四辆列车开出车站的所有可能的顺序。

java虚拟机中的堆,栈和方法区

基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。方法调用时传入的literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。字符串常量在方法区域分配,this 在堆空间分配。数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小! 哦对了,补充一下static在方法区域分配。 从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。 每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 <二> 这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有 比较大的帮助。 废话不想讲了.入主题: 先了解具体的概念: JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method) 堆区: 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) 2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 栈区: 1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 方法区: 1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。 为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。 AppMain.java

java内村分析(绝对经典)

浅析JVM内存结构和6大区域 内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C 之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收、GC),这对于开发人员来说确实大大降低了编写程序的难度,但带来的一个副作用就是,当系统运行过程中出现JVM抛出的内存异常(例如OutOfMemoryError)的时候,很难知道原因是什么,另外一方面,要编写高性能的程序,通常需要借助内存来提升性能,因此如何才能合理的使用内存以及让JVM合理的进行内存的回收是必须掌握的,本文将主 其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘。那JVM的内存结构到底是如何呢JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器、堆栈等区域。 JVM在运行时将数据划分为了6个区域来存储,而不仅仅是大家熟知的Heap区域,这6个区域图示如下: JVM内存的分配结构示意图 下面将逐一介绍下各个区域所做的工作及其充当的功能。

PC Register(PC寄存器) PC寄存器是一块很小的内存区域,主要作用是记录当前线程所执行的字节码的行号。字节码解释器工作时就是通过改变当前线程的程序计数器选取下一条字节码指令来工作的。任何分支,循环,方法调用,判断,异常处理,线程等待以及恢复线程,递归等等都是通过这个计数器来完成的。 由于Java多线程是通过交替线程轮流切换并分配处理器时间的方式来实现的,在任何一个确定的时间里,在处理器的一个内核只会执行一条线程中的指令。因此为了线程等待结束需要恢复到正确的位置执行,每条线程都会有一个独立的程序计数器来记录当前指令的行号。计数器之间相互独立互不影响,我们称这块内存为“线程私有”的内存。 如果所调用的方法为native的,则PC寄存器中不存储任何信息。 l JVM栈 JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址,因此Java中基本类型的变量是值传递,而非基本类型的变量是引用传递,Sun JDK的实现中JVM栈的空间是在物理内存上分配的,而不是从堆上分配。 由于JVM栈是线程私有的,因此其在内存分配上非常高效,并且当线程运行完毕后,这些内存也就被自动回收。 当JVM栈的空间不足时,会抛出StackOverflowError的错误,在Sun JDK中可以通过-Xss来指定栈的大小,例如如下代码: 1.new Thread(new Runnable(){ 2.public void run() { 3.loop(0); 4.} 5.private void loop (int i){ 6.if(i!=1000){ 7.i++; 8.loop (i); 9.} 10.else{ 11.return; 12.}

Java数据结构和算法笔记

~ Java数据结构和算法 第0讲综述 参考教材:Java数据结构和算法(第二版),[美] Robert lafore 1. 数据结构的特性 缺点 数据结构! 优点 数组插入快;如果知道下标,可以非常快地存取查找慢,删除慢,大小固定 有序数组比无序的数组查找快删除和插入慢,大小固定提供后进先出方式的存取存取其他项很慢 & 栈 队列提供先进先出方式的存取存取其他项很慢 链表插入快,删除快( 查找慢 二叉树查找、插入、删除都快(如果树保持平衡)删除算法复杂 红-黑树查找、插入、删除都快;树总是平衡的算法复杂 2-3-4树。 算法复杂 查找、插入、删除都快;树总是平衡的;类 似的树对磁盘存储有用 哈希表如果关键字已知,则存储极快;插入快删除慢,如果不知道关键字则存 储很慢,对存储空间使用不充分堆插入、删除快;对大数据项的存取很快对其他数据项存取慢 对现实世界建模有些算法慢且复杂 ' 图 2. 经典算法总结 查找算法:线性查找和二分查找 排序算法: 用表展示 ^ 第一讲数组 1.Java中数组的基础知识

1)创建数组 … 一旦创建数组,数组大小便不可改变。 2)访问数组数据项 数组数据项通过方括号中的下标来访问,其中第一个数据项的下标是0: 3)数组的初始化 当创建数组之后,除非将特定的值赋给数组的数据项,否则它们一直是特殊的null对象。 《 2.面向对象编程方式 1)使用自定义的类封装数组

)…

子问题须与原始问题为同样的事,且更为简单; b. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。 1.三角数字 该数列中的首项为1,第n项是由第n-1项加n后得到的。 1)使用循环查找第n项

Java关键字总结珍藏版(48个)

Java 48个关键字总结珍藏版 1.abstract Java 关键字 abstract 关键字可以修改类或方法。 abstract 类可以扩展(增加子类),但不能直接实例化。 abstract 方法不在声明它的类中实现,但必须在某个子类中重写。 示例 public abstract class MyClass { } public abstract String myMethod(); 注释 采用abstract 方法的类本来就是抽象类,并且必须声明为abstract。abstract 类不能实例化。 仅当abstract 类的子类实现其超类的所有abstract 方法时,才能实例化abstract 类的子类。这种类称为具体类,以区别于abstract 类。 如果abstract 类的子类没有实现其超类的所有abstract 方法,该子类也是abstract 类。 abstract 关键字不能应用于static、private 或final 方法,因为这些方法不能被重写,因此,不能在子类中实现。 final 类的方法都不能是abstract,因为final 类不能有子类。 2.boolean 变量的值可以是true 或false。 示例

boolean valid = true; if (valid) { } 注释 boolean 变量只能以true 或false 作为值。boolean 不能与数字类型相互转换。 包含boolean 操作数的表达式只能包含boolean 操作数。 Boolean 类是boolean 原始类型的包装对象类 3.break 关键字 用于提前退出for、while 或do 循环,或者在switch 语句中用来结束case 块。 示例 for (i=0; i) { break; } } int type = ;

计算机技术之堆与栈的详细介绍

java中堆(heap)和堆栈(stack)有什么区别

stack 和heep都是内存的一部分 stack 空间小,速度比较快,用来放对象的引用 heep大,一般所有创建的对象都放在这里。 栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量. 在java中,所有基本类型和引用类型都在栈中存储.栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域). 堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中. 在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象.所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用. 1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见

第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 3. Java中的数据类型有两种。 一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。 另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义: int a = 3; int b = 3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a 的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。

java基础知识详细整理(图文并茂深入浅出)

JSE第一部分 分享者:张振羽 2017.6.30

1.什么是 JDK API (1) Application Programming Interface ,应用程序编程接口 (2) 是一些预先定义的函数,开发人员可提供直接调用的功能。 2.JDK包结构 便于维护,按照功能划分,不同功能的累划分在不同功能的包中,常用的包如下表: 3.字符串的基本操作 3.1 String 及其API

3.1.1 String (1) String是不可变对象 (2) https://www.360docs.net/doc/de15672934.html,ng.String使用了final修饰,不能被继承。 (3)字符串一旦被初始化,就不可以被改变,存放在方法区中的常量池中 (4) 任何一个字符对应2个字节的长度,1个字节 = 8位二进制。 3.1.2 String s=new String (“abc”) (1) 众所周知,答案是:创建两个对象 (2) why?举例子: 分析步骤: a) 栈中开辟一块空间存放引用str1(地址信息); b) String池中有一块空间,存放String常量"abc"; c) 引用str1指向池中String常量"abc"; d) str1所指代的地址即常量"abc"所在地址,输出为true; 结论:创建了一个引用对象str1

分析步骤: 1) 栈中开辟一块空间存放引用str3; 2) 堆中开辟一块空间存放一个新建的String对象"abc"; 3) 引用str3指向堆中的新建的String对象"abc"; 4) str3所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出false; 3.2 StringBuilder常见API

JAVA内存分析

java内存分析 (2011-12-06 20:16:19) 转载▼ 标签: 杂谈 Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识。一般Java在内存分配时会涉及到以下区域: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 ◆堆:存放用new产生的数据 ◆静态域:存放在对象中用static定义的静态成员 ◆常量池:存放常量 ◆非RAM存储:硬盘等永久存储空间 Java内存分配中的栈 在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 Java内存分配中的堆 堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变 量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。 引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是Java 比较占内存的原因。 实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针! 常量池(constant pool) 常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如: ◆类和接口的全限定名; ◆字段的名称和描述符; ◆方法和名称和描述符。 虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。 对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的,对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。说到这里,对常量池中的字符串值

堆内存与栈内存的区别

栈内存与堆内存(Java) 2009-08-07 15:40 Java把内存划分成两种:一种是栈内存,一种是堆内存。 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由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()来新建对象的,它会在存放于堆中。每调用一次就会

基础类型包装类及java中equals和==的区别

java中equals和==的区别 值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中。 ==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。 equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同。==比较的是2个对象的地址,而equals比较的是2个对象的内容。 显然,当equals为true时,==不一定为true; 一、String中的equals和== 1、 public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; } } 上面这段程序中,到底有几个对象呢? 来检测一下吧,稍微改动一下程序 public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); } } 编译并运行程序,输出:s1 == s2 说明:s1 与s2 引用同一个String 对象-- "Monday"! 2.再稍微改动一下程序,会有更奇怪的发现: public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2"); } }

相关文档
最新文档