内存管理的学习

一、C和C++是需要手动进行内存管理:

为什么说c和c++是内存不安全的:

        有指针,有 delete[], 没有类型安全概念,这是不安全的最大因素;C++和C是不要求类型安全的,甚至编辑器都不报错。比如 A,和B无继承关系,但A*可以强制转化为 B*。指针是最大的不安全因素,delete[]的存在说明没有很好的内存管理,没有垃圾回收机制,容易内存泄露
主要是野指针和内存泄露。

        误用野指针。 用free或delete释放了内存后,还继续使用这块内存,这是有问题的,在内存释放后一定要把指针设置为NULL,避免野指针

        配了内存之后忘记释放内存是可能发生的,造成内存泄漏,如果内存泄漏严重,就会造成内存不足或内存耗尽,所以malloc/free、new/delete一定要配对使用。

        还比如 两个函数都使用一个对象B, 如果第一个函数把B给delete[]了,第二个函数就可能导致程序崩溃,因为他不知道该对象不存在了。。。仍然会使用,安全的语言不存在delete[] ,对象不用释放。

内存泄漏:是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。(危害)

C语言中动态内存管理方式:

malloc、calloc、realloc和free。malloc函数从上动态分配内存。

1.变量:

  • 全局变量(外部变量):出现在代码块{}之外的变量就是全局变量。
  • 局部变量(自动变量):一般情况下,代码块{}内部定义的变量就是自动变量,也可使用auto显示定义。
  • 静态变量:是指内存位置在程序执行期间一直不改变的变量,用关键字static修饰。代码块内部的静态变量只能被这个代码块内部访问,代码块外部的静态变量只能被定义这个变量的文件访问。

2.函数:

注意:C语言中函数默认都是全局的,可以使用static关键字将函数声明为静态函数(只能被定义这个函数的文件访问的函数)。

3.内存: 常量储存区(存放的是常量,不允许修改,如字符串"abc")、(全局/静态)区、堆、栈、自由存储区(C++才有)

栈区(stack):— 由编译器自动分配释放 ,

堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收

全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, - 程序结束后由系统释放。

而malloc函数从堆上动态分配内存。

 malloc、calloc、realloc和free 函数:

malloc的功能是开辟指定字节大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。传参时只需传入需要开辟的字节个数

calloc的功能也是开辟指定大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。calloc函数传参时需要传入开辟的内存用于存放的元素个数和每个元素的大小。calloc函数开辟好内存后会将空间内容中的每一个字节都初始化为0。

realloc可以调整已经开辟好的动态内存的大小,第一个参数是需要调整大小的动态内存的首地址,第二个参数是动态内存调整后的新大小。realloc函数与上面两个函数一样,如果开辟成功便返回开辟好的内存的首地址,开辟失败则返回NULL。

free的作用就是将malloc、calloc以及realloc函数申请的动态内存空间释放,其释放空间的大小取决于之前申请的内存空间的大小。
 

C++中动态内存管理方式:

通过new和delete操作符

C++相比于c多了一个:自由存储区(C++才有)

自由存储区:C++层面上的术语,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。new的申请是调用的malloc,自由存储区就和堆类似,但不等价。

new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从上动态分配内存。凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。

那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。

如:

C++ : int* p1 = new int; //申请                                        delete p1; //销毁

C     :int* p2 = (int*)malloc(sizeof(int)); //申请                 free(p2); //销毁


共同点: 都是从堆上申请空间,并且需要用户手动释放。

二、java和C# 的自动内存管理(管理的核心:当堆中的某个对象不再被使用到的时候,可以自动的将这块内存回收,置成可用状态。)        

由C,C++开发经历的同学,肯定对当时碰到的内存访问越界或者内存泄漏深恶痛绝,哪怕后续有了智能指针这些东西,还是不能完全避免此类问题。而C#和Java拥有的自动内存管理机制,让程序员可以不必自己去管理内存,专注于功能开发。

关于java的内存不安全:

java的内存管理就是对象的分配和释放

分配:内存的分配是程序完成的,程序员通过new关键字为对象申请内存空间(基本数据类型除外),对象都是在堆(Heap)中分配空间;释放:对象的释放是由垃圾回收机制(Garbage Collection)来做的,GC为了能正确释放对象,需要监控每一个对象的运行状况,包括申请,引用,被引用,赋值等。

java它能够处理大部分场景下的内存清理、内存泄露以及内存优化。但它也并不是万能的。java还是无法完全解决内存泄漏(内存泄漏通俗的来说就是堆中的一些对象已经不会再被使用了,但垃圾收集器却无法将它们从内存中清除)。在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。

内存泄漏很严重的问题,因为它会阻塞内存资源并随着时间的推移降低系统性能。如果不进行有效的处理,最终的结果将会使应用程序耗尽内存资源,无法正常服务,导致程序崩溃,抛出java.lang.OutOfMemoryError异常。

堆内存中通常有两种类型的对象:被引用的对象和未被引用的对象。被引用的对象是应用程序中仍然具有活跃的引用,而未被引用的对象则没有任何活跃的引用。

垃圾收集器会回收那些未被引用的对象,但不会回收那些还在被引用的对象。这也是java内存泄露发生的源头。

Java中内存泄露分类场景:

1.未关闭的资源:无论什么时候当我们创建一个连接或打开一个流,JVM都会分配内存给这些资源。比如,数据库链接、输入流和session对象。忘记关闭这些资源,会阻塞内存,从而导致GC无法进行清理。特别是当程序发生异常时,没有在finally中进行资源关闭的情况。

2.使用finalize()方法会存在潜在的内存泄露问题,每当一个类的finalize()方法被重写时,该类的对象就不会被GC立即回收。GC会将它们放入队列进行最终确定,在以后的某个时间点进行回收。如果避免此种情况发生呢?始终避免使用finalizer。

3String的intern方法:如果读取了一个大字符串对象,并且调用其intern方法,intern()会将String放在JVM的内存池中(PermGen),而JVM的内存池是不会被GC的。同样会造成程序性能降低和内存溢出问题。

4.静态属性导致内存泄露:会导致内存泄露的一种情况就是大量使用static静态变量。在Java中,静态属性的生命周期通常伴随着应用整个生命周期(除非ClassLoader符合垃圾回收的条件)。

通过虚拟机进行内存管理:

所谓内存管理,必然是运行时的事情, 而C#和Java之所以可以做到自动管理,就是因为它们在真正的机器二进制OS上有了自己的运行时(虚拟机: C#的CLR 和 Java的JVM)。

                  

1.java和C#的内存

 比较大的两块分别是堆和栈

java:

        堆内存用来存放由new创建的对象实例和数组。(重点)Java堆是所有线程共享的一块内存区域,在虚拟机jmv   启动时创建(,此内存区域的唯一目的就是存放对象实例 。Java堆是垃圾收集器管理的主要区域。 堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.

         在栈内存中保存的是堆内存空间的访问地址,或者说栈中的变量指向堆内存中的变量(Java中的指针)(重点)。

         方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 (重点)。

栈都是线程私有的,每个方法在执行时都会创建一个栈帧用于存储局部变量、操作数栈、方法出口等等信息。栈的生命周期与线程相同,而栈中存放的内存,在各自属于的方法执行完成后,就会自动释放。

堆是所有线程共享的。几乎所有的引用对象实例都是存放在这个区域的。而CLR或者JVM中下了大力气来管理的就是堆这块的内存区域,这种管理机制一般被称之为垃圾回收

2.垃圾回收机制就是用来管理堆中的内存了,当堆中的某个对象不再被使用到的时候,可以自动的将这块内存回收,置成可用状态。

3.那么如何知道某个对象不再被用到了呢,这个一般都是基于可达性分析后生成可达对象图来实现的;可达性分析就是通过从一些根对象出发,去遍历每一个被引用到的对象,当一个对象到根对象没有任何引用路径的时候,就认为该对象可以被回收

4.回收算法上面提到,通过可达性分析可以标记出所有需要回收的对象。那么如何回收呢?最简单的当然就是清除掉需要回收的对象的内存空间即可,这种方法实现简单,而且因为不改变存活的对象的地址,可以大大减少GC时的延时。但是带来的问题也很严重,它会让内存碎片化,最后导致虽然还有很多可用的内存,但是会导致没有连续的可用的大内存。

另外一种方法,就是需要对内存进行压缩处理,这种方法可以大大提高内存的利用率,并且为后续的内存分配带来便利(之后从已用空间的后面追加分配即可)。

对于内存的压缩处理,其实也可以分为两种处理手段,复制和整理。

复制算法就是把还存活的对象复制到另外一块内存中,它不太适合垃圾回收时有大量存活对象的情况,因为在这种情况下会需要准备大量的可用内存空间供复制使用。但是对于很少有存活对象的情况下,此种算法非常高效。

整理就是在清楚了可回收对象后,将存活对象的位置移动,使得他们的内存成为连续的一整块,这种算法在很少可回收对象的情况下使用比较广泛。

5.java程序是否会出现内存泄露:
  会出现内存泄漏。一般来说内存泄漏有两种情况。一是在堆中分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉;另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。

2、java的JVM为什么可以进行内存管理JVM内存管理机制 - 知乎

1.做java后台开发,对jvm的考察就是最重要的了!面试官肯定会问你对jvm的理解,你就要从以下几个角度来描述:

  1. JVM内存的划分
  2. 垃圾回收问题(定义、回收的东西)
  3. GC算法

JVM在Java程序的过程中会把它所管理的内存划分为5个不同的数据区域。

2、垃圾回收器(Garbage collection,GC)

垃圾回收机制是jvm自带的一个线程,用于回收没有被引用的对象。

3、需要垃圾回收的区域:

只有方法区与堆。因为程序计数器、JVM栈、本地方法栈的生命周期是和线程同步的,随着线程的销毁,它们占用的内存会自动释放,所以只有方法区和堆需要进行垃圾回收。

4.垃圾回收算法(4种):

  1. 标记-清除算法
  2. 复制算法
  3. 标记-整理算法
  4. 分代收集算法(重要)

5.如何判断对象是否是垃圾

使用两种算法判断:(1)程序计数器 (2)可达性分析(相比于第一种更常用)

三、rust无需内存管理

在 Rust 中每个值都只能被一个所有者拥有,当这个值被赋给其他所有者,原所有者无法再使用。正是这种机制保证了 Rust 语言的内存安全,从而无需自动垃圾回收,也无需手动释放。所有权是 Rust 最重要的特性之一

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值