c++和Java的对象内存异同

##前言

好久都没有写博客了。经过一段时间的实习,收获上感觉并不是很大,还好自己也有看一些东西。因为毕业设计做的是c++方面的编程,算是初探c++的内容。这里简单记录自己对c++和java的对象内存区别,谈谈c++为什么性能要比java高。想到哪里就记到哪里,因为很多东西自己也要查证才能确定。

##1.栈和堆

栈和堆在数据结构上是两种不同的结构,栈的特点是先进后出,而堆则可以看做是一大堆数据的集合。在c++和Java的内存中,也有堆和栈的概念。
首先理解一下,内存中栈中的每一个元素都被称为栈帧,每个栈帧中存储一个方法(function)中的用到的变量内存。程序执行时,就是一个栈中的每一帧被读取执行的过程。堆则是主要用来存储对象。那么在c++和java中到底有什么不同呢?

“`c++
object* processObject(int param){
object obj;
obj.param = param;
return &obj;
}

int main(){
object* obj = processObject(10);
obj->do_something(); //1
}

<pre><code><br />1处是会出现一个空指针错误的。而如果这是一段java的代码则完全没有问题。

在c++和java中,一个栈帧被执行完,这部分内存就直接被释放了。而在c++中,所有通过声明产生的对象都是在栈上开辟内存空间,所以函数执行完后这个对象也就被释放了,那么返回出来就是一个空指针了。只有通过new生成的对象才会在堆中申请空间,因此通过new出来的对象都需要在不用时手动释放内存,不然就会内存泄露。而在java中,对象是在堆中生成的(JDK7中的字符串是在常量池中,int等字面值在限定范围内也会在常量池中申请内存),所有方法内的对象都是一个指向堆中相应对象的指针(注意是指针而不是引用,很多人认为java中向方法传递参数是引用传递,但其实是值传递,只不过这个值是指向对象的指针)。而在java中不需要手动释放内存则是因为Java拥有GC机制(Garbage collect,垃圾回收)。这个在之后再谈。

##2 java的强弱引用和c++的智能指针

java的强弱引用和c++的智能指针都是在希望可以更好的进行内存管理的前提下出现的,java的强弱引用可以帮助GC机制进行粒度更细的内存回收,而c++的智能指针则是让没有GC机制的c++有了一定能力的自动释放内存的能力。
###a. java的强弱引用
java的引用共有四种,分别为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。
之所以定义出这么多引用,是希望在GC发生时,可以更灵活的进行对象的销毁。

强引用就是通过new出来的对象,强引用表示的都是必需的对象,这是无论如何都不会被回收的。

而软引用则表示有用但非必需的对象。当系统内存不够将要发生内存溢出异常的时候,会将软引用的对象列入回收范围,进行二次回收。只有这次回收仍然内存不足才会抛出内存溢出异常。JDK1.2之后提供了SoftReference类来实现这个。

弱引用也用来表示非必需的对象,不同的是,这个引用并不能帮助对象躲过任何的GC,也就是说无论如何,发生GC时这个对象都会被回收,弱引用的唯一作用就是用来取得一个对象实例。JDK1.2之后提供了WeakReference类来实现这个。

综合上述,虚引用的存在也比较清晰了。它不仅不能帮助对象躲过GC,甚至不能取得对象的实例。唯一存在的用处就是当其引用的对象被GC回收时会收到一个系统的通知。JDK1.2之后使用PhantomReference类来实现。

###b. C++的智能指针
相比于Java的GC机制以及为GC服务的各种引用,C++的智能指针则稍显简单。但是这个看似简单的c++的智能指针,对于c++来说意义也许并不是那么低。在我的理解中,c++之所以实用,因为相对于c,c++有着丰富高效的STL库和被大多数开发者接受的OOP。对于一个巨大的项目来说,OOP往往可以更好的帮助系统模块化,降低耦合,而STL库则是开发者进行高效工作的基础。相对于Java,单单从c++没有GC机制,就意味着它有着更高的性能,另外Java的虚拟机也是影响性能的一个方面。可也是因为c++没有GC机制,对于一个多人合作的巨大的项目来说,内存泄露则是面临的首要问题。举个很低端的例子
“`c++
class Example{
Mysql* getMysqlConnect(){
Mysql* mysql = new Mysql;
return mysql;
}
};

一个项目中所有的mysql对象指针都通过上面的getMysqlConnect()获取,看似没有问题,但是没有人敢保证一个项目中所有使用这个方法的人都会在使用完mysql对象后,手动将其释放。因此这里需要一个shared_ptr或者unique_ptr去包装一下指针,这样调用函数的人就不需要在外部释放对象内存,也就避免了内存泄露的问题。当然在使用shared_ptr时也一定要注意,因为可能会出现循环引用的问题,循环引用则会导致对象内存一直不被释放。
当然了,上面的例子只是为了说明这个例子而列举出来的,事实上可以直接返回对象而不是指针。
c++
class Example{
Mysql getMysqlConnect(){
Mysql mysql;
return mysql;
}
};

这里我一开始以为是会将对象复制一遍返回出来,但事实上在c++11中有了移动语义,即这种在拷贝语义和移动语义中,会优先使用移动语义来将mysql对象从方法中移出来,而不是拷贝mysql对象返回,并把方法中的对象删除。
##3 c++如何尽量避免内存泄露

后天再写啦

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据