在Java中,和内存相关的问题主要有两种,内存溢出和内存泄漏
内存溢出(Out Of Memory) :就是申请内存时,JVM没有足够的内存空间。通俗说法就是去蹲坑发现坑位满了。
内存泄露 (Memory Leak):就是申请了内存,但是没有释放,导致内存空间浪费。通俗说法就是有人占着茅坑不拉屎。
在JVM的几个内存区域中,除了程序计数器外,其他几个运行时区域都有发生内存溢出(OOM)异常的可能。
堆内存溢出
要解决这个内存区域的异常,常规的处理方法是首先通过内存映像分析工具(如JProfiler、Eclipse Memory Analyzer、MAT等)对Dump出来的堆转储快照进行分析。
溢出场景:内存泄漏、非内存泄漏
解决方案:通过工具分析,合理配置参数
栈内存溢出
java虚拟机规范:
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
如果虚拟机的栈内存允许动态扩展,当无法申请到足够内存时,将抛出OutOfMemoryError
栈能够分配的内存:
= 机器总内存 - 操作系统内存 - 堆内存 - 方法区内存 - 程序计数器内存 - 直接内存
方法区溢出
方法区:是被线程共享的,用来存储被虚拟机加载的类型信息、常量、静态变量等
不同jdk版本的方法区存放结构不同,相同的代码报错也可能不同
报错场景:常量池里对象太大、加载的类的"种类"太多、jsp项目、脚本语言动态类加载
如何避免方法区溢出:
根据JDK版本,为常量池保留足够空间 >=jdk7:设大Xms、Xmx
防止类加载过多导致的溢出 <=jdk7:设大PermSize、MaxPermSize
>jdk8:留空元空间相关的配置,或设置合理大小的元空间
直接内存溢出
直接内存:不属于虚拟机运行时数据区;是一块由操作系统直接管理的内存,也叫堆外内存;可以使用Unsafe或ByteBuffer分配直接内存;可用-XX:MaxDirectMemorySize控制,默认是0,表示不限制
为什么要用直接内存? 当然性能强于堆内存
直接内存使用场景
1.有很大的数据需要存储,生命周期很长
2.频繁的IO操作,比如并发网络通信
总结:
直接内存也叫堆外内存,IO效率较高
可以用Unsafe类或ByteBuffer来分配