A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 冷漠无天 中级黑马   /  2013-11-20 16:41  /  1981 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

内存管理原理: JAVA是一种强类型语言。java语言中,有java程序,java虚拟机,操作系统三个层次,其中java程序与java虚拟机进行交互,java虚拟机与操作系统进行交互,这也证实了java语言的跨平台特性。java程序运行前,java虚拟机向操作系统请求一定的内存空间,称为初始内存空间!程序执行过程中所需的内存都是由java虚拟机从初始内存空间进行划分的。当程序所需要的内存空间超过初始内存空间时,java虚拟机会再次向操作系统申请更多的内存空间供程序使用, 程序运行过程中,当java虚拟机已申请的内存达到规定的最大内存空间,java程序还需要更多的内存空间才能满足,此时就会出现内存溢出的错误! 从此可以看出,java程序所使用的内存空间都是由java虚拟机进行管理,分配的,java虚拟机规定了java程序的初始内存空间和最大内存空间,开发者只需要关心java虚拟机是如何管理内存空间的,而不用关心操作系统是如何管理内存的。Java内存划分: 在Java内存分配中,java将内存分为:方法区,堆,虚拟机栈,本地方法栈,程序计数器等。其中方法区和堆对于所有线程共享,而虚拟机栈和本地方法栈还有程序计数器对于线程隔离的。每个区域都有各自的创建和销毁时间。

一、程序计数器: 程序计数器是一块较小的内存空间,作用是当前线程所执行的字节码的行号指示器。Java的多线程是通过线程轮流切换并分配处理器执行时间方式来实现的。因此,每个线程为了能在切换后能恢复到正确的位置,每个线程需要独立的程序计数器。(程序计数器是线程私有的。如果线程正在 执行一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,则这个计数器是空(undefined))。 此内存区域是唯一的一个不会抛出OutOfMemoryError异常的区域。

二、Java虚拟机栈(Java Virtual Machine Stacks)
我们可能经常听到说java内存分为堆内存和栈内存,其实这个说法中的栈内存是指java虚拟机栈中的局部变量表部分。Java虚拟机栈描述的是java方法执行的内存模型:每个方法被执行时都会同时创建一个栈帧(Stack Frame),用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。Java虚拟机栈也是线程私有的,生命周期也线程相同。
局部变量表存放的类型包括以下三种:
1、 编译期可知的基本数据类型:boolean、byte、char、short、int、float、long、double共8 种类型;2、 对象引用:即reference类型,它存放的是一个指向堆中对象起始地址的引用指针,或一个代表对象的句柄或者其他与此对象相关的位置,根据虚拟机的不同实现而不同;3、 returnAddress类型:存放指向一条字节码指令的地址;局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量表空间是完全确定了的,方法在运行期间不会改变局部变量表的大小。Java虚拟机栈会抛出两种异常:1、OutOfMemoryError异常:如果虚拟本可以动态扩展,当扩展时无法申请到足够的内存时抛出;
2、StackOverflowError异常:如果线程请求的栈深度大于虚拟机所允许的深度时抛出;
三、本地方法栈:与虚拟机栈作用类似,区别在于虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机使用Native方法服务。

四、Java 堆: java堆是用于存放对象实例和数组。它是java虚拟机管理的内存中最大的一块,被所有线程共享,并且在虚拟机启动时创建。也是垃圾收集器管理的重要区域,几乎所有的对象实例都在这里分配内存。根据Java虚拟机规定,Java堆可以处于物理上不连续的内存空间,只要逻辑上连续即可。五、方法区: 方法区用于存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。它是堆的一个逻辑部分,是各个线程共享的内存区域。【 方法区中的运行时常量池(RuntimeConstant Pool),用于存放编译期生成的各种字面量和符号引用,它在类加载后存放到运行时常量池中。 运行时常量池具有动态性,即常量不一定只在编译期产生,在运行期间也可能将新的常量存入池中,比如String类的intern()方法】。
JAVA值传参与引用参数参数根据调用后的效果不同,即是否改变参数的原始数值,又可以分为两种:按值传递的参数与按引用传递的参数。按值传递的参数原始数值不改变,按引用传递的参数始数值改变!这是为什么呢?其实相当简单:我们知道基本数据类型的变量存放在栈里面,变量名处存放的就是变量的值,那么当基本数据类型的变量作为参数时,传递的就是这个值,只是把变量的值传递了过去,不管对这个值如何操作,都不会改变变量的原始值。而对引用数据类型的变量来说,变量名处存放的地址,所以引用数据类型的变量作为传参时,传递的实际上是地址,对地址处的内容进行操作,当然会改变变量的值了!经典实例public class StringTest { public static void main(String[] args) { String stu_Name = "zhangsan"; StringTest st= new StringTest (); System.out.println("方法调用前:" + stu_Name); st.strChange(stu_Name); System.out.println("方法调用后:" + stu_Name); } public void strChange(String str) { str = "lishi"; System.out.println("方法体中的str:" + str); } } 结果:方法调用前:zhangsan方法体中的str:lishi方法调用后:zhangsan
实例解析:虽然参数String 是引用数据类型,但其值没有发生改变,这是因为String 类是final 的,它是定长,我们看初始情况,即String stu_Name= "zhangsan";这行代码运行
完,
当调用方法时st.strChange(stu_Name);,内存变化为:
在方法体内,参数str赋予一个新值,str = "lishi"。因为String是定长,系统就会在堆中分配一块新的内存空间46DF,这样str指向了新的内存空间46DF,而stu_Name还是指向45DF, 46DF的改变对它已没影响:
最后,方法调用结束,str与46DF的内存空间消亡。stu_Name的值依然为zhangsan,并没有改变。所以String虽然是引用类型参数,但值依然不变。

点评

FFF
同学希望能看到你自己的学习笔记与心得体会。但内存这里对于我们初学者来说,还是太深入了一点。我们应该先打好基础,再来深入~!  发表于 2013-11-20 18:10

2 个回复

倒序浏览
我只是把好不容易看到的好东西给大家分享来,让大家看看即使现在用不到以后也能回过头来看看。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马