Java 内存管理原理、内存泄漏实例及解决方案研究

c语言关于freopen函数最全使用方法 hey,好久不见,最近沉迷游戏无法自拔,现在抽出时间来写写关于freopen函数的使用方法,这个freopen函数主要作用就是从文件读取函数同时写入文件,不像我们平常的控制台输入输出,也避免我们在刷一些题目时,面对大数据同时还不能复制的时候,就很方便啦! 二话不说 先上代码,这个代码解决的是: 这道题用于测试文件输入输出,请注意使用文件输入输出,而非标准输入输出。 输入一个正整数 a aa,输出这个数 a aa。 输入格式 第一行一个正整数 T TT,表示有 T TT 组测试数据。 接下来 T TT 行,每 阅读详情
 在项目的最后阶段,就是要防止系统的内存泄漏了,顺便找了些资料,看了些java内存泄漏的实例及解决,总结一下:

Java是如何管理内存

为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

为了更好理解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。


 
Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。

什么是Java中的内存泄露

下面,我们就可以描述什么是内存泄漏。在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。



因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。


Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
	Object o=new Object();
	v.add(o);
	o=null;	
}

//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。

Java内存泄漏的类型、实例及解决

1.对象游离

  一种形式的内存泄漏有时候叫做对象游离(object loitering),是通过清单 1 中的 LeakyChecksum 类来说明的,清单 1 中有一个 getFileChecksum() 方法用于计算文件内容的校验和。getFileChecksum() 方法将文件内容读取到缓冲区中以计算校验和。一种更加直观的实现简单地将缓冲区作为 getFileChecksum() 中的本地变量分配,但是该版本比那样的版本更加 “聪明”,不是将缓冲区缓存在实例字段中以减少内存 churn。该 “优化”通常不带来预期的好处;对象分配比很多人期望的更便宜。(还要注意,将缓冲区从本地变量提升到实例变量,使得类若不带有附加的同步,就不再是线程安全的了。直观的实现不需要将 getFileChecksum() 声明为 synchronized,并且会在同时调用时提供更好的可伸缩性。)

  清单 1. 展示 “对象游离” 的类

// BAD CODE - DO NOT EMULATE
public class LeakyChecksum {
 private byte[] byteArray;

 public synchronized int getFileChecksum(String fileName) {
  int len = getFileSize(fileName);
  if (byteArray == null || byteArray.length < len)
   byteArray = new byte[len];
  readFileContents(fileName, byteArray);
  // calculate checksum and return it
 }
}

  这个类存在很多的问题,但是我们着重来看内存泄漏。缓存缓冲区的决定很可能是根据这样的假设得出的,即该类将在一个程序中被调用许多次,因此它应该更加有效,以重用缓冲区而不是重新分配它。但是结果是,缓冲区永远不会被释放,因为它对程序来说总是可及的(除非 LeakyChecksum 对象被垃圾收集了)。更坏的是,它可以增长,却不可以缩小,所以 LeakyChecksum 将永久保持一个与所处理的最大文件一样大小的缓冲区。退一万步说,这也会给垃圾收集器带来压力,并且要求更频繁的收集;为计算未来的校验和而保持一个大型缓冲区并不是可用内存的最有效利用。

  LeakyChecksum 中问题的原因是,缓冲区对于 getFileChecksum() 操作来说逻辑上是本地的,但是它的生命周期已经被人为延长了,因为将它提升到了实例字段。因此,该类必须自己管理缓冲区的生命周期,而不是让 JVM 来管理。

  软引用

  弱引用如何可以给应用程序提供当对象被程序使用时另一种到达该对象的方法,但是不会延长对象的生命周期。Reference 的另一个子类 —— 软引用 —— 可满足一个不同却相关的目的。其中弱引用允许应用程序创建不妨碍垃圾收集的引用,软引用允许应用程序通过将一些对象指定为 “expendable” 而利用垃圾收集器的帮助。尽管垃圾收集器在找出哪些内存在由应用程序使用哪些没在使用方面做得很好,但是确定可用内存的最适当使用还是取决于应用程序。如果应用程序做出了不好的决定,使得对象被保持,那么性能会受到影响,因为垃圾收集器必须更加辛勤地工作,以防止应用程序消耗掉所有内存。

  高速缓存是一种常见的性能优化,允许应用程序重用以前的计算结果,而不是重新进行计算。高速缓存是 CPU 利用和内存使用之间的一种折衷,这种折衷理想的平衡状态取决于有多少内存可用。若高速缓存太少,则所要求的性能优势无法达到;若太多,则性能会受到影响,因为太多的内存被用于高速缓存上,导致其他用途没有足够的可用内存。因为垃圾收集器比应用程序更适合决定内存需求,所以应该利用垃圾收集器在做这些决定方面的帮助,这就是件引用所要做的。

  如果一个对象惟一剩下的引用是弱引用或软引用,那么该对象是软可及的(softly reachable)。垃圾收集器并不像其收集弱可及的对象一样尽量地收集软可及的对象,相反,它只在真正 “需要” 内存时才收集软可及的对象。软引用对于垃圾收集器来说是这样一种方式,即 “只要内存不太紧张,我就会保留该对象。但是如果内存变得真正紧张了,我就会去收集并处理这个对象。” 垃圾收集器在可以抛出 OutOfMemoryError 之前需要清除所有的软引用。

  通过使用一个软引用来管理高速缓存的缓冲区,可以解决 LeakyChecksum 中的问题,如清单 2 所示。现在,只要不是特别需要内存,缓冲区就会被保留,但是在需要时,也可被垃圾收集器回收:

清单 2. 用软引用修复 LeakyChecksum

public class CachingChecksum {
 private SoftReferencebufferRef;

 public synchronized int getFileChecksum(String fileName) {
  int len = getFileSize(fileName);
  byte[] byteArray = bufferRef.get();
  if (byteArray == null || byteArray.length < len) {
   byteArray = new byte[len];
   bufferRef.set(byteArray);
  }
  readFileContents(fileName, byteArray);
  // calculate checksum and return it
 }
}

2、基于数组的集合

  当数组用于实现诸如堆栈或环形缓冲区之类的数据结构时,会出现另一种形式的对象游离。清单 3 中的 LeakyStack 类展示了用数组实现的堆栈的实现。在 pop() 方法中,在顶部指针递减之后,elements 仍然会保留对将弹出堆栈的对象的引用。这意味着,该对象的引用对程序来说仍然可及(即使程序实际上不会再使用该引用),这会阻止该对象被垃圾收集,直到该位置被未来的 push() 重用。

  清单 3. 基于数组的集合中的对象游离
public class LeakyStack {
 private Object[] elements = new Object[MAX_ELEMENTS];
 private int size = 0;

 public void push(Object o) { elements[size++] = o; }
 
 public Object pop() {
  if (size == 0)
   throw new EmptyStackException();
  else {
   Object result = elements[--size];
   // elements[size+1] = null;
  return result;
 }
}
}

修复这种情况下的对象游离的方法是,当对象从堆栈弹出之后,就消除它的引用,如清单 3 中注释掉的行所示。但是这种情况 —— 由类管理其自己的内存 —— 是一种非常少见的情况,即显式地消除不再需要的对象是一个好主意。大部分时候,认为不应该使用的强行消除引用根本不会带来性能或内存使用方面的收益,通常是导致更差的性能或者 NullPointerException。该算法的一个链接实现不会存在这个问题。在链接实现中,链接节点(以及所存储的对象的引用)的生命期将被自动与对象存储在集合中的期间绑定在一起。弱引用可用于解决这个问题 —— 维护弱引用而不是强引用的一个数组 —— 但是在实际中,LeakyStack 管理它自己的内存,因此负责确保对不再需要的对象的引用被清除。使用数组来实现堆栈或缓冲区是一种优化,可以减少分配,但是会给实现者带来更大的负担,需要仔细地管理存储在数组中的引用的生命期。

C/C++中的freopen()函数使用详解 目录C/C++重定向 标准输入输出 的库函数实战 C/C++重定向 标准输入输出 的库函数 所谓重定向输出,就是可以把原本只是输出在控制台的字符,输出到你指定的路径文件中。(输入类似,就是从指定的文件中读取,而不是读取在控制台中的输入。)重定向函数可以在任何时候开启、关闭。 函数名:freopen 标准声明:FILE *freopen( const char *path, const char *mode, FILE *stream ); 所在文件: <stdio.h> 参数说明: path: 阅读详情

相关推荐

C++ freopen 打开txt文件,读取,写入txt文件

需要stdio头 freopen属于输入输出流的重定向,一般我们默认的输入流是在黑黑的控制台,但是我们也可以选择从一个txt文本中读取信息,这和我们键入控制台是一样的,相当于把txt的内容拷贝到控制台 参数 /* param filePath : 文件路径(string) param mode : 模式 r=读 w=写(string) param stream : 重定向到那个流 std...

AKGWSB 's blog 7086

java的垃圾回收(GC)详解

比方 说分成三个、四个、五个?Java 堆(Java Heap)是JVM所管理的内存中最大的一块,堆又是垃圾收集器管理的 主要区域,这里我们主要分析一下 Java 堆的结构: Java 堆主要分为2个区域-新生代与老年代(java1.8之后),其中新生代又分 Eden 区和 Survivor 区,其中 Survivor 区又分 From 和 To 2个区。如果该对象被引用,则它的引用计数加 1,如果删除对该对象的引用,那么它的引用计数就减1,当该对象的引用计数为0 时,那么该对象就会被回收。

LJWfbj666的博客 4626

C语言的freopen使用测试及C++的文件流简单使用

文件操作使用参考,供课程设计学生参考。 文本文件1.txt内容如下4行,放在d盘根目录: 1 test 2 file 3 abcde 4 12345 一、C的freopen使用测试: #include #include using namespace std; int main() { freopen("d:\\1.txt","r",stdin);//打开文件1.

寻找快乐 6187

Java GC详解

本文详细介绍了JVM中的垃圾回收机制,包括各类垃圾收集器、垃圾回收算法、结构、原理、运行流程、触发时机等内容。

1_5_2_的博客 2518

使用freopen函数重定向输入流

有那么一阵子,调试程序的时候经常遇到大量输入数据的情况,如果从控制台输入,将是非常耗时且容易出错,幸好有freopen函数,可以将输入重定向到文件中,也就是说,将输入数据放到文件中,调用freopen函数将输入从控制台重定向为文件,那么后续的从控制台读取输入数据的操作,将自动变为从文件中读取,不需要手动从控制台输入了。 用一个简单的例子,记录下freopen的用法: int main(in

longztysty的博客 902

关于CSP的初赛和复赛的注意事项(freopen及其他)

另外,需要注意 CCF 的测评机可能测评速度是较慢的,所以自己的代码如果发现更低的时间复杂度的算法且时间充裕没事干时,可以将自己更低时间复杂度的代码写出来,然后与自己的代码与高时间复杂度(但是能保证正确的,这点需要注意)进行对拍。保证程序的正确性,如果有错误,要么改改代码,要么果断放弃。希望大家能够在今年的 CSP 中取得好成绩,没有取得好成绩的也不要担心,毕竟,还有下一年呢!初赛是 CSP 中较为重要的一关,如果你没有通过,那么只能下次再来了……(地区我取的是我们当地的),所以,我们可以较为大胆的使用。

zhangkeyuan0623的博客 4386

【JAVA核心】Java GC机制详解

每个程序员都遇到过内存溢出的情况,程序运行时,内存空间是有限的,那么如何及时的把不再使用的对象清除将内存释放出来,这就是GC要做的事。

Never say never 7447

C++Fstream文件流与freopen重定向教程

大家好,这是我第一次写博客,请大家多多支持! 本文是作者原创文章,写了好几个小时,所以麻烦转载请注明出处! 目录 freopen重定向 fstream文件流 freopen 重定向 使用方法: freopen(“in.txt”,“r”,stdin);//从in.txt读入 freopen(“out.txt”,“w”,stdout);//向out.txt输出 freopen(“CON”,“r”...

EricNTH的博客 4003

java---垃圾回收算法(GC)

对垃圾回收算法,做了详细的原理介绍,特别是对于分代算法详细的介绍了他的基本的流程,以及需要注意的一些事项。

m0_58196614的博客 6583

freopen多文件输入输出

鉴于蒟蒻造数据时的痛苦经历,在这里整理了用freopen实现多文件输入输出的方法。

m0_73020012的博客 3313

freopen读取文件

调试往往不能一次成功,每次运行时,都要重新输入一遍测试数据,对于有大量输入数据的题目,输入数据需要花费大量时间。 使用freopen函数可以解决测试数据输入问题,避免重复输入 函数名:freopen 声明:FILE *freopen( const char *path, const char *mode, FILE *stream ); 所在文件: stdio.h 参数说明: path: 文件名,用于存储输入输出的自定义文件名。 mode: 文件打开的模式。 stream: 一个文件,通常使用标准流文件。

programmer222的博客 1110

Java垃圾回收机制(GC)

1.垃圾回收算法 1)标记清除算法: 标记:先进行扫描,将已存活的对象进行标记; 清楚:对堆内存进行遍历,回收没有标记的对象; 缺点:清楚后会产生大量不连续的内存碎片,也就是碎片化问题;这个问题可能导致后续创建较大的对象时无法找到足够的连续空间继而再次触发垃圾回收。 2)复制算法:将可用内存分为对象面和空闲面,在对象面上创建对象,若对象面满,就将还存活的对象复制到空闲面,然后将对象面的所有对象清楚。 这个算法解决了碎片化的问题,特别使用Eden区,因为Eden区对象的存活率很低,所以所需复制的对

w7486的博客 3516

freopen()重定向的打开和关闭

freopen函数 功能 使用不同的文件或模式重新打开流,即重定向。 实现重定向,把预定义的标准流文件定向到由path指定的文件中。(直观感觉/实际操作都像是把文件定向到流,难道是说,对流来说就是重定向,大雾)。 如果指定了新文件名,则该函数首先尝试关闭已与stream(第三个参数)关联的任何文件并取消关联。然后,无论该流是否成功关闭,freopen都会打开由filename指定...

dianshu1593的博客 1617

freopen()——重定向标准输入输出流

freopen()——重定向标准输入输出流   头文件:stdio.h 函数原型:FILE * freopen(const char *filename , const char *type ,  FILE *stream);        例: freopen(“in.txt”,”r”,stdin); freopen(“out.txt”,”w”,stdout);

yym951376975的专栏 1611

使用freopen重定向输入输出流

在做ACM算法题目时,经常需要测试大量输入数据。freopen函数可用于算法本地调试,等算法运行正确后便可将代码提交系统。freopen函数的主要功能是将预定义的标准流文件定向到由path指定的文件中。 FILE *freopen( const char *path, const char *mode, FILE *stream ); 参数说明: path: 文件名,用于存储输入输出

Ai_ViVi的专栏 906

C(C++)语言中freopen重定向的使用

1.函数原型 FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream); 2.参数说明 filename: 要打开的文件名,可以是绝度路径,也可以是相对路径(输入文件,即模式“r”,如果与你的C源文件在同一级目录下,可以使用) m

斗code、年华 2572

信息奥赛文件读写freopen(“in.txt“,“r“,stdin) 函数的用法

使用freopen函数可以解决测试数据输入问题,避免重复输入。 重定向输出,就是可以把原本只是输出在控制台的字符,输出到你指定的路径文件中。(输入类似,就是从指定的文件中读取,而不是读取在控制台中的输入。)重定向函数可以在任何时候开启、关闭。 函数名:freopen 声明:FILE *freopen( const char *path, const char *mode, FILE *stream ); 所在文件:stdio.h 参数说明: path:文件名,用于存储输入输出的...

星卯教育-信奥教练tony 5859

matlab相机标定_实战 | 相机标定

点击“计算机视觉life”关注,置顶更快接收消息!本文阅读时间约5分钟什么是相机标定?我们为什么需要相机标定?相机标定就是确定相机内参和外参的过程,其结果精度会直接影响视觉系统后续工作的准确性。坐标系相机标定涉及到了四大坐标系,分别为:像素坐标系图像物理坐标系相机坐标系世界坐标系为了进行相机标定,必须已知世界坐标系中足够多的三维空间点坐标,找到这些空间点在图像中投影点的二维图像坐标,并建...

weixin_39747334的博客 765
上一篇: 也谈基础!
下一篇: 调整 Java I/O 性能
bulinner
博客等级 码龄22年 6粉丝 29原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值