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 ...
随机推荐
- mysql 零碎笔记
聚合函数的用法: concat 连接单行记录的不同字段, group_concat 连接多行记录的相同字段, concat_ws count 按条件统计: SELECT COUNT(*) AS `nu ...
- 算法与数据结构(七) AOV网的拓扑排序(Swift版)
今天博客的内容依然与图有关,今天博客的主题是关于拓扑排序的.拓扑排序是基于AOV网的,关于AOV网的概念,我想引用下方这句话来介绍: AOV网:在现代化管理中,人们常用有向图来描述和分析一项工程的计划 ...
- SVG PATH 生成器
参考网站:http://dayu.pw/svgcontrol/ 主要功能:手动可视化生成 SVG图片PATH路径. 效果如下: 代码如下: <!DOCTYPE html> <!-- ...
- [Swift]LeetCode564. 寻找最近的回文数 | Find the Closest Palindrome
Given an integer n, find the closest integer (not including itself), which is a palindrome. The 'clo ...
- [Swift]LeetCode805. 数组的均值分割 | Split Array With Same Average
In a given integer array A, we must move every element of A to either list B or list C. (B and C ini ...
- TCP/IP 详解常用术语
业务需要,最近看TCP/IP 这本书,专业名词太多了,总结一下,给后来着参考,直接使用. 后续会在读书时慢慢添加. ACK:(ACKnowledgment)TCP首部中的确认标志. ARP:地址解析协 ...
- Redis 设计与实现 (三)--持久化
RDB 持久化 一.生成RDB cmd:SAVE --阻塞进程,执行完,才能有效接收客户端命令. cmd: BGSAVE --非阻塞,开启子进程保存. 客户端如果发送SAVE和BGSAVE命令直 ...
- python网络-计算机网络基础(23)
一.网络简介 网络是由节点和连线构成,表示诸多对象及其相互联系. 一个人玩: 两个人玩: 多个人玩: 说明 网络就是一种辅助双方或者多方能够连接在一起的工具 如果没有网络可想单机的世界是多么的孤单 使 ...
- 20行以内python代码画出各种减压图
一.太阳花 看到一个很有意思的代码,你若安好,便是晴天!太阳花向你开~ 绘画效果如下: 代码如下: from turtle import * color('red', 'yellow') begin_ ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...