JVM & Memory (2) java.exe

上篇说到了关于java heap的一些特征:连续的内存区域,逐步扩张的特征和如何做的这点的。but在讨论JVM的堆内存的细节以及垃圾回收等事宜之前,我们先讨论一下java进程的内存结构。

郁闷,
昨日 这一篇本来写完了,但是提交失败了。。。

只好再写了,简略些吧,继续正题:

通常我们所关注JVM的内存区域是java heap(堆),比如有时发生内存泄露(memory leak),或者内存溢出(out of memory)的问题,让我们不得不把堆的问题弄透彻。先提一个入门的小问题,而为什么c/c++就不用关注这个呢(它也有堆内存的申请)?那是因为, java语言为了让开发者省事,在语言设计上就没有提供内存释放的语句(比如delete/free那样的)并强制大家使用,而是采用了程序只管创建(
new )对象即可,由JVM的Garbage collector全权负责回收无用的对象的方式。与c/c++那样的构造/析构方式相比,两者各有利弊,此处不再废话。

然而堆内存只是java进程所占内存空间上的一部分,JVM还有哪些主要的内存区域呢?
首先是JVM native code segment ,这部分内存空间被jvm中各部件的二进制执行代码所占据,属于固定开销,我们无法更改或影响其所占的大小,具体都有哪些部件呢,如执行引 擎即解释器(exec engine/interpreter)、class loader、内存管理器、gc器、jit编译器、与OS的接口(主要有6大类:System/Memory/Library/Thread/File/Socket)等(这些部件都是老虎屁股,摸不得的);其次,是Java stack(栈 ) , 是解释器用于解释执行字节码指令的工作区域,也是本地变量的藏身之处(不明白什么是本地变量请自己翻书),一般每线程拥有独立的一段,如果线程很多,函数 调用深度很深,本地变量繁多,那么,java stack自然庞大,另外,为了防止java stack开销过大,一个线程的调用深度是有限制(一般jvm命令行可指定)的,超过了就会发生stack overflow,要说明的是,java stack是jvm native heap的一部分,当然,java heap也是 jvm native heap的 一部分;第三,与java stack相仿的是native stack ,不过它可不包含在jvm native heap之中,这是来自于jvm自身组件和native函数的普通code(非字节码的哦)在函数调用时必须要用到的栈(学过汇编语言的,这些都不难理 解),这部分内存我们也无能为力;还有一部分重要的区域是method area ,这部分内存存放了所有已加载class的字节码(注意class loader加载某类时,还会产生该类唯一的一个java.lang.Class实例,它存在于java heap中),这部分内存大小和系统运行时需要运行的类和代码的多寡有关,而它也不是一成不变的,当class gc不被禁止时,如果某类的Class对象实例被gc时,其method data也将被清除,而jit编译器或者hotspot编译器在运行时也会改动它们(以后再探讨这部分),显而易见,method area是jvm native heap中的一块;加上java heap以及class manifest等,jvm的内存基本上可以按以上划分区域,切个图表示一下吧 。言多必失,各类jvm实现毕竟有所不同,而jvm规范又总是比较abstract。

再来说一下你的程序运行时,数据都存储在哪里(此时你也该明白了吧)。不考虑有native方法的情形(以后再细说它),那么:

  • class中的byte code会放在method area中(不用担心实例方法的重合,实例方法并不特殊于静态方法,只不过第一个参数隐含是this,而jvm用数字给本地变量命名,this的终身编号 总是1);
  • 本地变量( local variable )的 (对于原生类型,就是数据本身,若是引用类型则只是它的引用指针值,下同)存在于java stack中;
  • 而所有的 成员变量(instance field和static field)的 所有的对象实例(Object,注意也包含一个class的Class对象)本身 都存在于java heap中
总的看来,大批量的数据(尤其是生命周期不长的动态的数据)还是存放在java heap上的,对于java heap,一个看似矛盾的事实是,原生数据(primitive)无法直接存在于java heap上,而java heap却主要地存放着它们。后面将专门写一篇关于java heap的。

编末:重写一遍,和原来的内容大相径庭啊,不过神没散就成。

从以上,我们基本可以先得出一部分best practices:
  • 缩减你的系统中class的总个数。jvm随着版本升级,越来越庞大了,单是一个xml parser,类的个数已经成百上千。多一个类,意味着在java heap之外,仍然多一份内存开销。当你看到一个系统一开始运行就装载上万个class时,你的心情一定不轻松。
  • 降低类之间的关联度,包括继承、域引用和方法调用,静态域也要注意释放(赋值为null),不仅在类加载和初始化获得效率,对于服务器程序来说,也使得class gc能够顺利进行。在多线程、热部署、长时间运行的大型环境下,稍不留神,JVM里就会发生极其龌龊的事情。

再次修改注:静态变量也是在java heap上的,原稿误解了。

你可能感兴趣的:(java,jvm,多线程,c,C#)