小白一枚,请多指教
小白今日任务:熟悉StackOverflowError与OutOfMemoryError
1、重现StackOverflowError
找到StackOverflowError源码
package java.lang; /** * Thrown when a stack overflow occurs because an application * recurses too deeply. * * @author unascribed * @since JDK1.0 */ public class StackOverflowError extends VirtualMachineError {
根据源码注释可以知道产生StackOverflowError原因是 because an application recurses too deeply,翻译过来也就是递归层数过深,知道原因之后就可以写一个很简单的demo,如下
public static void main(String[] args) { main(null); }
"如愿以偿"的得到了我们想要的StackOverflowError
Exception in thread "main" java.lang.StackOverflowError
2、探索StackOverflowError
1)源码中只是轻描淡写的一句because an application recurses too deeply 会造成StackOverflowError,那到底递归的临界点在哪里呢?
既然是栈内存溢出,想找到临界点,我们就得找寻栈内存的大小,参考oracle官方文档给出的关于HotSpot的表格:
可以看到"0 means use default stack size",也就是说在不做任何设置的情况下,就使用默认的栈内存,也就是如果是主流64位系统栈内存默认是1024KB(用户可以通过-Xss设置栈内存大小)
2)知道了栈内存后,那到底深度递归做了什么会”压死“栈内存呢?
首先要知道栈内存中都存储着什么?通过查阅资料,知道每次调用方法都会产生”栈帧“压入栈内存中,”栈帧“的大小取决于方法的参数、局部变量和算法,可以简单地把专业名词”栈帧“理解为方法,在深度递归中,大量的方法压入了栈内存并且短时间无法得到释放,加上默认的栈内存只有可怜的1M,很快就会消耗完栈内存,造成异常
3、重现OutOfMemoryError
同样我们也找到OutOfMemoryError的源码
package java.lang; /** * Thrown when the Java Virtual Machine cannot allocate an object * because it is out of memory, and no more memory could be made * available by the garbage collector. * * {@code OutOfMemoryError} objects may be constructed by the virtual * machine as if {@linkplain Throwable#Throwable(String, Throwable, * boolean, boolean) suppression were disabled and/or the stack trace was not * writable}. * * @author unascribed * @since JDK1.0 */ public class OutOfMemoryError extends VirtualMachineError {
可以很直接的看到"no more memory could be made","cannot allocate an object"这些语句,表示JVM已经无法为对象分配内存而造成异常
为了最快速度Full GC报错,可以调整下JVM内存大小,如下在IDEA中调整运行时JVM内存为10M
接下来,占满10M我们只要制造一个超过10M的对象即可,如下新建一个11M的byte数组
public static void main(String[] args) { byte[] bytesArray = new byte[11 * 1024 * 1024]; }
"如愿以偿"的得到了我们想要的OutOfMemoryError
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
4、探索OutOfMemoryError
1)我们也如法炮制的找一下堆内存默认的临界值,堆内存大小我们可以借助api查询,如下为源码部分截取
/** * Returns the total amount of memory in the Java virtual machine. * The value returned by this method may vary over time, depending on * the host environment. * <p> * Note that the amount of memory required to hold an object of any * given type may be implementation-dependent. * * @return the total amount of memory currently available for current * and future objects, measured in bytes. */ public native long totalMemory(); /** * Returns the maximum amount of memory that the Java virtual machine will * attempt to use. If there is no inherent limit then the value {@link * java.lang.Long#MAX_VALUE} will be returned. * * @return the maximum amount of memory that the virtual machine will * attempt to use, measured in bytes * @since 1.4 */ public native long maxMemory();
根据源码的注释,totalMemory可以获取当前程序运行JVM使用的内存,maxMemory可以获取JVM可以使用的最大内存,
默认情况下JVM最大会占用系统内存的四分之一,因为JVM大部分内存都分配给堆使用,所以我们可以粗略的认为查询结果即为堆内存,JVM的调优就是在堆内存的分配上面下功夫
2)引入到JVM,这次的探索也就差不多了,漫漫JAVA路的开头,JVM还是有点遥远的东西,希望有一天JVM探索可以出现在我的日记里,希望那时已不是小白,仅此以记,与君共勉^^