Java开发笔记(七十四)内存溢出的两种错误
前面介绍的几种异常,其实都存在这样那样的逻辑问题,属于程序员的编码手误。还有一大类系统错误,表面上看不出什么问题,但是程序仍然运行不下去,兹举二例说明。
第一个例子且看下列的测试代码:
// 测试内存溢出错误:程序需要的内存超过了最大的堆内存配置
private static void testUnlimitedString() {
String str = "Hello world";
String result = getUnlimitedString(str); // 获取无限大小的字符串
System.out.println("result="+result.toString());
} // 获取无限大小的字符串
private static String getUnlimitedString(String str) {
System.out.println("getUnlimitedString");
String append = String.format("%s+%s", str, str);
return getUnlimitedString(append);
}
执行测试代码中的testUnlimitedString方法,一开始程序正常打印日志,然而不一会儿就报错退出了,错误信息为“java.lang.OutOfMemoryError: Java heap space”,意思是内存溢出。仔细阅读测试代码,发现其中的getUnlimitedString方法会调用自身,从而形成了递归调用。要命的是,方法递归的同时不断拼接更长的字符串append;而这意味着,每次递归调用之后,新的append串长度都要翻番;经过多次调用,append串所需的存储空间以指数级别增长,于是没多久便撑爆了程序所能用到的内存了。
第二个例子依旧先看下面的下面的测试代码:
// 测试栈溢出错误:程序占用的栈空间超过了配置的栈内存大小
private static void testUnlimitedRecursion() {
recursionAction(); // 用于递归动作的方法
} // 用于递归动作的方法
public static void recursionAction() {
System.out.println("recursionAction");
recursionAction();
}
执行测试代码中的testUnlimitedRecursion方法,结果还是很快就报错退出了,错误信息为“java.lang.StackOverflowError”,意思是栈溢出。可是第二个例子在递归调用中并未拼接字符串,为啥仍旧出现溢出错误了呢?这是因为程序在运行时会申请两块内存空间,一块叫堆内存,另一块叫栈内存;其中堆内存承包了程序运行所需的大部分存储需求,包括变量、数组、对象实例等等;而栈内存仅仅负责保管每次方法调用的现场数据,包括方法自身、方法的输入参数、方法内部的基本变量等等,并在方法调用结束时释放该方法占用的内存空间。前述的第一个例子,它的内存溢出发生于堆内存;至于后面的第二个例子,它的内存溢出发生于栈内存。
那么为什么方法调用的有关数据放在栈内存而不是堆内存呢?举个现实生活中的例子,假设一对小夫妻带着宝宝回家过年,随身携带的物品都放在行李箱里,则行李箱就是属于他们的堆内存。然后一家三口准备坐动车回来,在路上还得处理一些事情,每件事情都相当于一次方法调用。例如在车站买车票,用手掏出钱包,抽出人民币付款买完车票,再把钱包塞回去。在这个买车票的方法中,输入参数是钱包,输出参数是车票,而手充当了栈内存的角色。买票之前,两手空空;买票的过程中,一只手抓着钱包;买完票后,钱包塞回去,两手又变空了。打电话也可看作是方法调用,打电话前,两手空空;打电话的时候,一只手握住手机通话;打完电话,收好手机,两手依然空空。此时双手属于分配给他们的栈内存,由于有两只手,因此栈内存的大小为二,即最多同时办理两件事情。
一家三口检票上车,女人有事走开了一会儿,这时宝宝饿得大哭,男人赶紧泡奶给宝宝喂。只见这个奶爸先用左手抱着宝宝,再用右手扶着奶瓶,相当于喂奶事件拥有方法嵌套,外层的喂奶方法占用了左手这块栈内存,内层的扶奶瓶方法又占用了右手这块栈内存。宝宝还在喝奶的时候,苦逼的奶爸忽然内急,于是抱着宝宝一边喂奶一边飞奔至厕所,站在马桶面前准备小便,猛然发现两只手都在忙,无法解手。只有等宝宝喝完奶,右手把奶瓶放旁边,这样空出来的右手才能帮忙方便。但是宝宝喝奶的方法还没结束调用,上厕所的方法已经等不及了,怎么办怎么办?可怜的奶爸情急之下只好尿裤子了,对程序来说便发生了栈内存溢出。要么有个路人伸出援手(栈内存大小加一),一把扯下奶爸的裤子,方能避免尿裤子的尴尬(栈溢出的错误)。
总结一下,凡是编码问题造成的程序崩溃,都归类为异常Exception;凡是系统不堪重负造成的程序崩溃,都归类为错误Error。不过异常与错误仅是分类上的区别,实际开发中,二者的扔出和捕捉操作无甚差别,所以若没有特殊情况,今后将使用“异常”一词统称异常与错误。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(七十四)内存溢出的两种错误的更多相关文章
- Java开发笔记(十四)几种运算符的优先级顺序
到目前为止,我们已经学习了Java语言的好几种运算符,包括算术运算符.赋值运算符.逻辑运算符.关系运算符等基础运算符,并且在书写赋值语句时都没添加圆括号,显然是默认了先完成算术.逻辑.关系等运算,最后 ...
- Java开发笔记(九十四)文件通道的性能优势
前面介绍了字节缓存的一堆概念,可能有的朋友还来不及消化,虽然文件通道的用法比起传统I/O有所简化,可是平白多了个操控繁琐的字节缓存,分明比较传统I/O更加复杂了.尽管字节缓存享有缓存方面的性能优势,但 ...
- 【Java学习笔记之三十四】超详解Java多线程基础
前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...
- 树莓派开发笔记(十四):入手研华ADVANTECH工控树莓派UNO-220套件(三):使用研发自带系统测试rtc、gpio、232和485套件接口
前言 上一篇说明了必须要使用研华自带的8G卡的系统,通过沟通拿到了相关的系统,购买的时候会带8GB的卡,请自行备份一份镜像.本篇对uno-220套件的相关研华配套的额外接口做测试,篇幅较长,重点讲 ...
- Java开发学习(二十四)----SpringMVC设置请求映射路径
一.环境准备 创建一个Web的Maven项目 参考Java开发学习(二十三)----SpringMVC入门案例.工作流程解析及设置bean加载控制中环境准备 pom.xml添加Spring依赖 < ...
- Java开发笔记(十八)上下求索的while循环
循环是流程控制的又一重要结构,“白天-黑夜-白天-黑夜”属于时间上的循环,古人“年复一年.日复一日”的“日出而作.日落而息”便是每天周而复始的生活.计算机程序处理循环结构时,给定一段每次都要执行的代码 ...
- 【Java学习笔记之十四】Java中this用法小节
用类名定义一个变量的时候,定义的只是一个引用,外面可以通过这个引用来访问这个类里面的属性和方法. 那们类里面是够也应该有一个引用来访问自己的属性和方法纳? 呵呵,JAVA提供了一个很好的东西,就是 t ...
- Java开发笔记(十九)规律变化的for循环
前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
随机推荐
- Linux Rabbit的使用
安装RabbitMQ 1.安装Erlang yum -y install epel-release yum -y update yum -y install erlang socat yum -y i ...
- 对matplotlib库的运用
1.matplotlib库的运用效果图 绘制基本的三角函数 ...
- .net core2.1 使用 dynamic 类型报错
在net core2.0项目中使用 dynamic 无法编译通过 异常信息:缺少编译器要求的成员"Microsoft.CSharp.RuntimeBinder.CSharpArgumentI ...
- 一文读懂四种常见的XML解析技术
之前的文章我们讲解了<XML系列教程之Schema技术_上海尚学堂java培训技术干货><XML的概念.特点与作用.XML申明_上海Java培训技术干货>,大家可以点击回顾一下 ...
- [Swift]LeetCode521. 最长特殊序列 Ⅰ | Longest Uncommon Subsequence I
Given a group of two strings, you need to find the longest uncommon subsequence of this group of two ...
- EPPlusHelper
public class EPPlusExcelHelper : IDisposable { public ExcelPackage ExcelPackage { get; private set; ...
- 学习Python--函数进阶
函数进阶 目标 函数参数和返回值的作用 函数的返回值 进阶 函数的参数 进阶 递归函数 01. 函数参数和返回值的作用 函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形 ...
- 什么是“闭包”(closure)为什么要用它?
什么是闭包: 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方 ...
- 10 个深恶痛绝的 Java 异常。。
异常是 Java 程序中经常遇到的问题,我想每一个 Java 程序员都讨厌异常,一 个异常就是一个 BUG,就要花很多时间来定位异常问题. 什么是异常及异常的分类请看这篇文章:一张图搞清楚 Java ...
- Xapian使用入门
关键字:搜索引擎.Xapian 一篇拖了两三年的入门总结文章,今天发出来,一方面是自己的总结,另一方面是给自己和他人的备忘.读者需要对搜索引擎有初步了解,譬如了解倒排.term.doc.相似度打分等概 ...