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数据库表的修改及删除
一.对数据表的修改 1.重命名一张表: RENAME TABLE 原名 TO 新名字; ALTER TABLE 原名 RENAME 新名; ALTER TABLE 原名 RENAME TO 新名; 2 ...
- python MVC、MTV 框架介绍 Django 模板系统常用语法
Django 框架简介一.MVC框架和MTV框架1.MVC 全名Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分.优势: 耦合性低 重用性高 生命 ...
- webapi 知识点
在web api 中后台的方法必须 加入 [HttpGet] ,[HttpPost],[HttpPut],[HttpDelete] 来区分,这是一种习惯. ps: get 方式参数都存在http协议头 ...
- Mesos源码分析(7): Mesos-Slave的启动
Mesos-Slave的启动是从src/slave/main.cpp中的main函数开始的. 看过了Mesos-Master的启动过程,Mesos-Slave的启动没有那么复杂了. 1. ...
- 从协议入手,剖析OAuth2.0(译 RFC 6749)
1.介绍 https://tools.ietf.org/html/rfc6749 传统的client-server授权模型,客户端通过使用凭证(通常的用户名和明文密码)访问服务端受保护的资 ...
- [Swift]LeetCode98. 验证二叉搜索树 | Validate Binary Search Tree
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...
- [Swift]LeetCode875. 爱吃香蕉的珂珂 | Koko Eating Bananas
Koko loves to eat bananas. There are N piles of bananas, the i-th pile has piles[i]bananas. The gu ...
- Kubernetes---Pod控制器
Pod作为kubernetes的最基本单元,它的控制器有以下这些 Pod的控制器: 1, RC ( ReplicationController): 2, RS(ReplicaSet) : 3, De ...
- 机器学习入门 - Google机器学习速成课程 - 笔记汇总
机器学习入门 - Google机器学习速成课程 https://www.cnblogs.com/anliven/p/6107783.html MLCC简介 前提条件和准备工作 完成课程的下一步 机器学 ...
- .NET Core 2.0迁移技巧之web.config配置文件
大家都知道.NET Core现在不再支持原来的web.config配置文件了,取而代之的是json或xml配置文件.官方推荐的项目配置方式是使用appsettings.json配置文件,这对现有一些重 ...