热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

c++和Java数组内存空间申请

c中由于数组导致的内存溢出处理之前实现图论算法的时候碰到过超大的整型数组,用c去实现着实会碰到一些莫名其妙的内存溢出问题。例如:我们想定义一个200

c++中由于数组导致的内存溢出处理

之前实现图论算法的时候碰到过超大的整型数组,用c++去实现着实会碰到一些莫名其妙的内存溢出问题。

例如:我们想定义一个 2000*2000 的 2d 数组,甚至是这样大小的 2d 的 vector,如果是直接定义成局部变量,那么将会直接报内存溢出错(大家可以计算一下这样大小的数组会占据多少内存空间)。

那么为什么定义成局部变量会导致内存溢出呢?笔者认为是因为程序中函数的调用或者说是运行其实是在栈中执行的,而栈的空间是很有限的,所以说定义过大的局部变量必然导致栈溢出(Stack overflow)错误。

因此对于较大的局部变量我们需要将其定义为全局的,尤其在函数的递归调用中。亲测 2d vector 当大小超过100,就已经能够导致溢出错了(vector 中的自增长方法会开辟更大的空间)。

众所周知,数组是一种顺序存储的数据结构,在定义数组时,首先要确定数组的大小。前面提到的静态数组在编译时就需要确定数组的大小,所以,为了防止内存溢出,我们尽量将数组定义的大一些,但是这样太过浪费内存。甚至当超过一定大小之后,编译会报错,例如直接定义一个全局的:

int a[3000][3000];

这样程序可能连编译都无法通过。

这样大型的数组需要通过定义动态数组。动态数组不需要在编译时就确定大小,它的大小在程序运行过程中确定,所以可以根据程序需要而灵活的分配数组的大小,相比静态数组,它更“灵活”、“自由”。但是动态数组需要进行显式的内存释放。

int **Ctable; // 全局的二次指针
void DynamicCreate2Array()
{// 二维动态数组的空间开辟 3000*3000大小int m &#61; 3000, n &#61; 3000;int i;cout << "############## call the CTable create func #############" << endl;//动态开辟空间 Ctable &#61; new int*[m]; //开辟行 for (int i &#61; 0; i < m; i&#43;&#43;)Ctable[i] &#61; new int[n]; //开辟列 int val &#61; 0;for (i &#61; 0; i < m; i&#43;&#43;)for (int j &#61; 0; j < n; j&#43;&#43;){Ctable[i][j] &#61; 0;val &#43;&#43;; } cout << "items: " << val << endl;
}
int main(){DynamicCreate2Array(); // 为CTable动态开辟内存int count &#61; 3000;//初始化Ctable全为0for(int i &#61; 0; i < count; i&#43;&#43;)for(int j &#61; 0; j < count; j&#43;&#43;)Ctable[i][j] &#61; 0;// 输出Ctablefor(int i &#61; 0; i < count; i&#43;&#43;)for(int j &#61; 0; j < count; j&#43;&#43;)printf("%d\t", Ctable[i][j]);//释放Ctable开辟的资源 for (int i &#61; 0; i < 3000; i&#43;&#43;)delete[] Ctable[i];delete[] Ctable;}

Java 中对大数组的处理

Java 程序在运行时都要开辟空间&#xff0c;任何软件在运行时都要在内存中开辟空间&#xff0c;Java虚拟机运行时也是要开辟空间的。JVM运行时在内存中开辟一片内存区域&#xff0c;启动时在自己的内存区域中进行更细致的划分&#xff0c;因为虚拟机中每一片内存处理的方式都不同&#xff0c;所以要单独进行管理。

JVM内存的划分有五片&#xff1a;


  1. 寄存器

  2. 本地方法区

  3. 方法区

  4. 栈内存

  5. 堆内存

我们重点来说一下堆和栈&#xff1a;


栈内存

栈内存首先是一片内存区域&#xff0c;存储的都是局部变量&#xff0c;凡是定义在方法中的都是局部变量&#xff08;方法外的是全局变量&#xff09;&#xff0c;for循环内部定义的也是局部变量&#xff0c;是先加载函数才能进行局部变量的定义&#xff0c;所以方法先进栈&#xff0c;然后再定义变量&#xff0c;变量有自己的作用域&#xff0c;一旦离开作用域&#xff0c;变量就会被释放。栈内存的更新速度很快&#xff0c;因为局部变量的生命周期都很短。


堆内存

存储的是数组和对象&#xff08;其实数组就是对象&#xff09;&#xff0c;凡是new建立的都是在堆中&#xff0c;堆中存放的都是实体&#xff08;对象&#xff09;&#xff0c;实体用于封装数据&#xff0c;而且是封装多个&#xff08;实体的多个属性&#xff09;&#xff0c;如果一个数据消失&#xff0c;这个实体也没有消失&#xff0c;还可以用&#xff0c;所以堆是不会随时释放的&#xff0c;但是栈不一样&#xff0c;栈里存放的都是单个变量&#xff0c;变量被释放了&#xff0c;那就没有了。堆里的实体虽然不会被释放&#xff0c;但是会被当成垃圾&#xff0c;Java有垃圾回收机制不定时的收取。

下面我们通过一个图例详细讲一下堆和栈&#xff1a;

比如主函数里的语句

int [] arr &#61; new int [3];

在内存中是怎么被定义的&#xff1a;

主函数先进栈&#xff0c;在栈中定义一个变量arr,接下来为arr赋值&#xff0c;但是右边不是一个具体值&#xff0c;是一个实体。实体创建在堆里&#xff0c;在堆里首先通过new关键字开辟一个空间&#xff0c;内存在存储数据的时候都是通过地址来体现的&#xff0c;地址是一块连续的二进制&#xff0c;然后给这个实体分配一个内存地址。数组都是有一个索引&#xff0c;数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化&#xff08;这是堆内存的特点&#xff0c;未初始化的数据是不能用的&#xff0c;但在堆里是可以用的&#xff0c;因为初始化过了&#xff0c;但是在栈里没有&#xff09;&#xff0c;不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体&#xff1a;
在这里插入图片描述
那么堆和栈是怎么联系起来的呢?

我们刚刚说过给堆分配了一个地址&#xff0c;把堆的地址赋给arr&#xff0c;arr就通过地址指向了数组。所以arr想操纵数组时&#xff0c;就通过地址&#xff0c;而不是直接把实体都赋给它。这种我们不再叫他基本数据类型&#xff0c;而叫引用数据类型。称为arr引用了堆内存当中的实体。&#xff08;可以理解为c或c&#43;&#43;的指针&#xff0c;Java成长自c&#43;&#43;和c&#43;&#43;很像&#xff0c;优化了c&#43;&#43;&#xff09;

如果当int [] arr&#61;null;

arr不做任何指向&#xff0c;null的作用就是取消引用数据类型的指向。

当一个实体&#xff0c;没有引用数据类型指向的时候&#xff0c;它在堆内存中不会被释放&#xff0c;而被当做一个垃圾&#xff0c;在不定时的时间内自动回收&#xff0c;因为Java有一个自动回收机制&#xff0c;&#xff08;而c&#43;&#43;没有&#xff0c;需要程序员手动回收&#xff0c;如果不回收就越堆越多&#xff0c;直到撑满内存溢出&#xff0c;所以Java在内存管理上优于c&#43;&#43;&#xff09;。自动回收机制&#xff08;程序&#xff09;自动监测堆里是否有垃圾&#xff0c;如果有&#xff0c;就会自动的做垃圾回收的动作&#xff0c;但是什么时候收不一定。

所以堆与栈的区别很明显&#xff1a;

1.栈内存存储的是局部变量而堆内存存储的是实体&#xff1b;

2.栈内存的更新速度要快于堆内存&#xff0c;因为局部变量的生命周期很短&#xff1b;

3.栈内存存放的变量生命周期一旦结束就会被释放&#xff0c;而堆内存存放的实体会被垃圾回收机制不定时的回收。

import java.util.Scanner;public class MyArray {public static void main(String[] args) {// 动态数组int col, row;Scanner sc &#61; new Scanner(System.in);String line &#61; sc.nextLine();String [] nums &#61; line.split(" ");col &#61; Integer.parseInt(nums[0]);row &#61; Integer.parseInt(nums[1]);System.out.println(col &#43; row);int [][] bigArray &#61; new int[col][row];for (int i &#61; 0; i < col; i&#43;&#43;) {for (int j &#61; 0; j < row; j&#43;&#43;) {bigArray[i][j] &#61; i &#43; j;}}System.out.println(bigArray[col-1][row-1]);}
}
/* 输入与输出
3000 3000
6000
5998
*/


推荐阅读
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了在Java中gt、gtgt、gtgtgt和lt之间的区别。通过解释符号的含义和使用例子,帮助读者理解这些符号在二进制表示和移位操作中的作用。同时,文章还提到了负数的补码表示和移位操作的限制。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 本文整理了Java中java.lang.NoSuchMethodError.getMessage()方法的一些代码示例,展示了NoSuchMethodErr ... [详细]
  • 本文整理了Java中org.apache.pig.backend.executionengine.ExecException.<init>()方法的一些代码 ... [详细]
author-avatar
茶香未散尽_385_312
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有