除了Java堆和永生代之外,还有一些区域会占用较多的内存,而这些内存总和可能受到操作系统进程最大的内存限制。比如,一个服务器内存2G,其中1.6G分配给Java堆,另外的0.4G分配给操作系统和下面的区域,那么0.4G很可能不够用。例如当Direct Memory占用过多的内存时,虚拟机虽然会对Direct Memory进行回收,但是却不像新生代,老年代一样,发现空间不足了就通知收集器进行垃圾回收,它只能等待老年代满了后进行Full GC,然后顺便帮它清理一下内存中的废弃对象。否则,它只有等到抛出内存溢出异常后,先catch掉,再在catch块里大喊一声:System.gc();若是虚拟机没有执行,它就只有看着堆中还有很多空闲内存,而自己却抛出内存溢出异常。例如:NIO操作需要使用到Direct Memory内存。
1、Direct Memory:其是堆外缓存Big Memory的一个实现,它能够在内存中序列化大批量对象,而不影响JVM垃圾回收的性能。可通过-XX:MaxDirectMemorySize调整大小,内存不足时抛出OutOfMemoryError或者OutOfMemoryError:Direct buffer memory。
2、线程堆栈:可通过-Xss调整大小,内存不足时抛出StackOverflowError(无法分配新的栈帧)或者OutOfMemoryError:unable to create new native thread(无法建立新的线程)。
3、Socket缓存区:每个Socket连接都Receive和Send两个缓存区,分别占大约37kb和25kb内存,连接多的话这块内存占用也比较客观,如果无法分配,则可能会抛出IOException:Too many open files异常。
4、JNI代码:如果代码中使用JNI调用本地库,那本地库使用的内存也不在堆中。
5、虚拟机和GC:虚拟机和GC的代码执行也需要一定的内存。