J2EE基本概念(4)

【4】Java内存管理 学习JAVA的人都知道,JAVA开创了一个极具特色的功能就是垃圾回收功能,也就是GC,以前的C/C++的程序,你分配出来的内存你都必须进行自己管理。自己管理的意义就是你申请的内存你一定要释放掉。这就带了一个问题,当程序很大的时候,你创建的对象被很多地方使用,特别是你new出来的,你并且不知道在特定的逻辑下是否会走到释放的循环中去时,你就有可能造成内存泄漏。而内存泄漏的结果就是程序运行到一定时间会crash.JAVA为我们考虑得很好,这主要还得益于JAVA内存空间的合理规划分配。如下图所示:

image

如上图所示,我们知道JAVA通过JAVA编译器生成class文件,这些CLASS文件实际上是对源文件中定义的类的方法和状态的编译结果。JAVA使用类装载器将这些类装入到虚拟机分配的内存空间中,也叫runtime data areas,在这一块区域内存中,以前像C不会存在一块由虚拟机管理的内存空间,它通常都是在真实的操作系统内存空间里。因此,一些常用的服务器应用如Weblogic/tomcat都提供了VM参数用来调整这一块区域的大小。防止缺省的空间太小。其中方法区(method area)通常包括类型信息、常量池(是一个根据类型定义的常量的有序常量集,包括字面量(string、integer\float)以及符号引用(类型、字段、方法),整个常量池会被JVM的一个索引引用,如同数组里面的元素集合按照索引访问一样,JVM针对这些常量池里面存储的信息也是按照索引方式进行)、字段信息、方法信息、类变量(static)。方法区如果想要在C/c++内存中找到对应,那应该就是符号表和常量区。与C/C++不一样的,是JAVA编译后CLASS文件它并不是一个可以直接由操作系统执行的,它是依赖于JVM调用JNI,JNI调用本地类库实现的。因此,JAVA运行里本身依赖于JNI代码来实现类库功能。这些本地方法调用的状态信息是存储在一个独立的本地方法内存栈中(native method stacks),运行时本地方法和其它内存存储的数据保持独立,并且本地方法编译后字节码根据不同的操作系统是不一样的。注意这里的本地方法区通常是自己编写的JNI C/C++程序编译后被JVM装入的内存空间,它不是JVM内部实现需要调用的底层方法库,那个是native method libraries. 通常JAVA一个程序起动后,它由JVM来创建一个唯一个线程,从JVM的角度来说,多个JAVA程序运行的是一个独立的进程,每个进程含有一个唯一的线程(非多线程程序角度下),当这个线程启动后,JVM内部分配一个PC寄存器(程序寄存器)和JAVA椎栈(JAVA STACKS)。若该线程正在执行一个非本地JAVA方法,在PC寄存器的值指示下一条指令执行,该线程在JAVA内存栈中保存了非本地JAVA方法调用状态,包括局部变量、被调用参数、返回值及中间计算结果。内存堆(heap)是指当一个JAVA应用在运行的时候在程序中创建一个对象或者一个数组的时候,JVM会针对该对象和数组分配一个新的内存空间。JVM内存堆跟C/C++中堆是不一样的,就是JVM拥有针对新的对象分配内存的指令,但是不包含释放该内存空间的指令,当然在开发过程可以在JAVA代码显示释放内存(使用final),但实际上也不是真正释放,JVM仅仅是强制做了一次检测,具体什么时候释放完全由垃圾回收器来处理。

JAVA同C/C++中不一样,就是JAVA不存在指针类型。那么定义在JAVA stacks中JAVA类变量对象,怎么样与分配在内存椎上数据与分配在方法中的类信息进行关联呢。JAVA也没有单独列出来一个引用类型像C++一样。那么怎么办呢?这里回到一个原点,就是JAVA是一个比C++更面向对象的语言,它抛弃了C++中一些容易混淆的概念,希望采用一种纯粹的OO概念。当然是不可能绝对OO。所以说在JAVA里对象是主角,除了一些基本数据类型,当然它也在后面JDK5提供BOXing功能后变成全是对象。也就是所有的变量都是对象变量。对象变量与对象数据结合起来没有指针,怎么办?JAVA采用内部数据结构来进行关联。对外来说,一个对象变量引用一个对象内容。这里引用有点指针的意思,但是不是指针,也不是引用类型概念。它大提上相当于关联到。英语中都使用reference,可以叫参考,有人叫引用,但是不要同C++中引用类型混淆,那个是一个相同对象但不同名称的概念。那么这里说JAVA内部将这三种东东通过一个数据结构进行关联到一起,这里就涉及到堆的三种设计。如下图所示一:

image

如上图所示,方法区类信息和堆中对象池中对象信息都在一个句柄池中进行保存,并跟栈中对象变量也就是上图中object refrenece进行关联。这种实现简单明了,只是需要占用堆空间。

image

这第二种堆实现其实同第一种没有太大的区别,唯一个就是不再存在单独的句柄池,直接在每个对象实例数据结构体上设置指向方法区方法类型信息的指针。还有最后一种就是将这个结构体移到方法区,如下图所示:

image 

在这种设计结构上比较适合于多态类或者重载情况,因为这时候是运行时才能知道方法区具体调用的类信息。

你可能感兴趣的:(职场,J2EE,休闲)