JVM一向很好的帮我们管理内存,它就是一个贤内助:“向政府(内存空间)能要到地盘,还能有效的对自己的一亩三分地进行管理。”但是有时候呢,我们不懂怜香惜玉的一而再再而三的向它施压,把我们的一切不管好的坏的都扔给它,但是呢它也没有怨言,只是在地实在放不下的时候会悄悄的告知我们:“StackOverFlowError或者OutOfMemoryError。”既然它如此劳心劳力的付出,我们是不是也应该知道为什么它要抛出来这些异常呢?又或者我们通过一些方式避免掉这些异常让我们的JVM更欢快的奔跑在它的一亩三分地上呢! 那在这之前,或许我们应该了解下它所处一亩三分地周围的环境。
寄存器用于存储计算单元执行指令的中间结果,寄存器大小决定了一次计算的可使用的最大值,连接RAM和处理器或者寄存器和处理器的叫做地址总线,地址总线决定了处理器最大的寻址空间,32位总线宽度可以拥有2的32次方个内存位置,所以32位总线可以拥有4G的内存空间。 每个程序运行的时候会向系统申请一段独立的内存空间,但随着程序的庞大和任务的复杂性,物理内存无法满足需求,此时就有了虚拟内存,虚拟内存可以使多个进程在同时运行时可以共享物理内存,这里的共享只是空间上的共享,逻辑上它们依然独立,当然虚拟内存还可以扩展内存空间。 系统会将不经常活动的程序的数据移到一个磁盘文件中,将真正的物理内存留给真正运行的程序用,但当唤醒这个程序时,os又会将磁盘上数据重新交换到物理内存上,此时磁盘会吱吱的响,但我们要避免这种情况的出现,效率会很低,在linux服务器上我们经常关注swap分区,swap分区如果被经常使用,系统就会非常缓慢,表示系统内存严重不足,或者某些程序没有即时释放内存。 那么我们可以看出物理内存对于程序来说十分重要,而物理内存又会分为内核空间和用户空间,内核空间保证操作程序的调度和硬件的逻辑连接,我们的程序自然而然的使用的是用户空间,所以每一次调用都会存在两块空间的切换,比如一次网络传输,内核空间先接收远程主机传来的数据,再从内核空间复制到用户空间供程序使用,这种情况虽然保证了安全,但是效率很低,当然现在有很多种方式来提升效率减少复制。
那么正题来了,在我们的Java组件中是如何使用内存呢?
一.堆
众所周知,堆是用来存储Java对象的地方,它的大小在JVM启动时就一次性的向系统申请完成,-Xmx表示堆的最大大小,-Xms表示初始化大小。一旦申请完成,堆的大小就固定,不能在内存不够时再向系统申请,当然同理内存空闲的时候它也不会将内存还回去,就是自成世界了,另立门户了,生死由己!
二.线程
在用一段简单代码看JVM的执行过程 这篇文章中我们知道每个线程创建时都会创建一个私有的栈来存储数据,在HotSpot这款JVM中不区分虚拟机栈和本地方法栈,栈容量大小通过-Xss设定。
三.类和加载器
类和类加载器也被存储在堆中,这个区域叫永久代(PermGen区)。 注意的是:一个jar包的类用哪个JVM就加载那一个,不会一股脑的全加载完,理论上使用的类越多,Java使用的类越多,需要占用的内存也越多。
四.NIO
NIO使用的内存是本机内存而不是Java堆上的内存,另外NIO的ByteBuffer产生的数据和网络或者磁盘交互的时候都在操作系统的内核空间发生,不需要复制到本机内存,如果我们需要发送很小的数据效率会比较高。 我们JVM运行时要处理的数据主要关注点在运行时数据区,在下一章,我将继续学习Java内存分配结构和Java内存分配策略
文章转自:https://blog.csdn.net/sureSand/article/details/78715056