欢迎光临
我们一直在努力

.net中的托管资源与非托管资源,如何避免内存泄露

托管资源与非托管资源

“非托管资源”指的是不受.net垃圾回收器控制的资源。比如打开一个数据库连接,所访问的数据库资源来源于服务器而不受.net控制。或者是客户机上其他非.net资源,资源的提供者不是通过托管代码编写的。

这就是为什么对于数据库连接,需要写在一个using中

using (var connection = new SqlConnection("connection_string_here")) { connection.open(); //..do something... connection.close(); }

这样using执行完后会调用连接对象的.Dispose()方法,确保所有非托管资源被清除。

以上代码等价于

var connection = new SqlConnection("connection_string_here") try { connection.open(); //..do something... connection.close(); } finally { if (connection != null) ((IDisposable)connection).Dispose(); }

常见的非托管资源有:文件句柄、数据流、固定内存、COM 对象、数据库连接、网络连接

这些非托管资源如果没有显式地清理他们,将会造成数据泄露和资源占用。

对于托管资源,则无需担心其何时被销毁,当一个对象实例没有被任何变量引用时,系统会自动对其进行垃圾回收

释放非托管资源方法

下面用一个例子来介绍一下.NET非托管资源回收方法

将包含非托管对象的类继承IDisposable接口,IDisposable接口要求实现Dispose方法

Stream抽象类本身也继承了IDispose接口,如果是要创建一个自定义的Stream类,则它会继承父类的所有接口,包括IDispose;在以下编写的类WrappingStream中,定义了一个Stream类型的成员m_streamBase,用于接收非托管资源,

有两个作用:

1.如果外部的传入的非托管资源已释放,WrappingStream内部的流不会释放,保证BinaryReader能够完全控制这个流。

2.由于WrappingStream继承IDispose,在使用该对象时,包在using中,当using执行完后释放内部的流时,不会导致外部的流也释放,保证外部的流供其他程序正常使用

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace MyStreamClass { /// <summary> /// A <see cref="Stream"/> that wraps another stream. The major feature of <see cref="WrappingStream"/> is that it does not dispose the /// underlying stream when it is disposed; this is useful when using classes such as <see cref="BinaryReader"/> and /// <see cref="System.Security.Cryptography.CryptoStream"/> that take ownership of the stream passed to their constructors. /// </summary> public class WrappingStream : Stream { /// <summary> /// Initializes a new instance of the <see cref="WrappingStream"/> class. /// </summary> /// <param name="streamBase">The wrapped stream.</param> public WrappingStream(Stream streamBase) { m_streamBase = streamBase ?? throw new ArgumentNullException("streamBase"); } /// <summary> /// Gets a value indicating whether the current stream supports reading. /// </summary> /// <returns><c>true</c> if the stream supports reading; otherwise, <c>false</c>.</returns> public override bool CanRead { get { return m_streamBase == null ? false : m_streamBase.CanRead; } } /// <summary> /// Gets a value indicating whether the current stream supports seeking. /// </summary> /// <returns><c>true</c> if the stream supports seeking; otherwise, <c>false</c>.</returns> public override bool CanSeek { get { return m_streamBase == null ? false : m_streamBase.CanSeek; } } /// <summary> /// Gets a value indicating whether the current stream supports writing. /// </summary> /// <returns><c>true</c> if the stream supports writing; otherwise, <c>false</c>.</returns> public override bool CanWrite { get { return m_streamBase == null ? false : m_streamBase.CanWrite; } } /// <summary> /// Gets the length in bytes of the stream. /// </summary> public override long Length { get { ThrowIfDisposed(); return m_streamBase.Length; } } /// <summary> /// Gets or sets the position within the current stream. /// </summary> public override long Position { get { ThrowIfDisposed(); return m_streamBase.Position; } set { ThrowIfDisposed(); m_streamBase.Position = value; } } /// <summary> /// Begins an asynchronous read operation. /// </summary> public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return m_streamBase.BeginRead(buffer, offset, count, callback, state); } /// <summary> /// Begins an asynchronous write operation. /// </summary> public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return m_streamBase.BeginWrite(buffer, offset, count, callback, state); } /// <summary> /// Waits for the pending asynchronous read to complete. /// </summary> public override int EndRead(IAsyncResult asyncResult) { ThrowIfDisposed(); return m_streamBase.EndRead(asyncResult); } /// <summary> /// Ends an asynchronous write operation. /// </summary> public override void EndWrite(IAsyncResult asyncResult) { ThrowIfDisposed(); m_streamBase.EndWrite(asyncResult); } /// <summary> /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// </summary> public override void Flush() { ThrowIfDisposed(); m_streamBase.Flush(); } /// <summary> /// Reads a sequence of bytes from the current stream and advances the position /// within the stream by the number of bytes read. /// </summary> public override int Read(byte[] buffer, int offset, int count) { ThrowIfDisposed(); return m_streamBase.Read(buffer, offset, count); } /// <summary> /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// </summary> public override int ReadByte() { ThrowIfDisposed(); return m_streamBase.ReadByte(); } /// <summary> /// Sets the position within the current stream. /// </summary> /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param> /// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"/> indicating the reference point used to obtain the new position.</param> /// <returns>The new position within the current stream.</returns> public override long Seek(long offset, SeekOrigin origin) { ThrowIfDisposed(); return m_streamBase.Seek(offset, origin); } /// <summary> /// Sets the length of the current stream. /// </summary> /// <param name="value">The desired length of the current stream in bytes.</param> public override void SetLength(long value) { ThrowIfDisposed(); m_streamBase.SetLength(value); } /// <summary> /// Writes a sequence of bytes to the current stream and advances the current position /// within this stream by the number of bytes written. /// </summary> public override void Write(byte[] buffer, int offset, int count) { ThrowIfDisposed(); m_streamBase.Write(buffer, offset, count); } /// <summary> /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// </summary> public override void WriteByte(byte value) { ThrowIfDisposed(); m_streamBase.WriteByte(value); } /// <summary> /// Gets the wrapped stream. /// </summary> /// <value>The wrapped stream.</value> protected Stream WrappedStream { get { return m_streamBase; } } /// <summary> /// Releases the unmanaged resources used by the <see cref="WrappingStream"/> and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected override void Dispose(bool disposing) { // doesn't close the base stream, but just prevents access to it through this WrappingStream if (disposing) m_streamBase = null; base.Dispose(disposing); } private void ThrowIfDisposed() { // throws an ObjectDisposedException if this object has been disposed if (m_streamBase == null) throw new ObjectDisposedException(GetType().Name); } Stream m_streamBase; } } 

在使用这个类时,将Stream作为一个方法传入参数传给WrapStream并实例化

private void Create(Stream streamSource) { Random rnd = new Random(); var connections = new int[] { (int)ConnectionType.Tab, (int)ConnectionType.Blank }; png = null; imageSource = null; var uri = new Uri(destFileName); //We do this to avoid memory leaks using (WrappingStream wrapper = new WrappingStream(streamSource)) using (BinaryReader reader = new BinaryReader(wrapper)) { imageSource = new BitmapImage(); imageSource.BeginInit(); imageSource.CacheOption = BitmapCacheOption.OnLoad; imageSource.StreamSource = reader.BaseStream; // streamSource; imageSource.EndInit(); imageSource.Freeze(); } imgShowImage.Source = imageSource; scvImage.Visibility = Visibility.Hidden; cnvPuzzle.Visibility = Visibility.Visible; var angles = new int[] { 0, 90, 180, 270 }; int index = 0; for (var y = 0; y < rows; y++) { for (var x = 0; x < columns; x++) { if (x != 1000) { int upperConnection = (int)ConnectionType.None; int rightConnection = (int)ConnectionType.None; int bottomConnection = (int)ConnectionType.None; int leftConnection = (int)ConnectionType.None; if (y != 0) upperConnection = -1 * pieces[(y - 1) * columns + x].BottomConnection; if (x != columns - 1) rightConnection = connections[rnd.Next(2)]; if (y != rows - 1) bottomConnection = connections[rnd.Next(2)]; if (x != 0) leftConnection = -1 * pieces[y * columns + x - 1].RightConnection; int angle = 0; var piece = new Piece(imageSource, x, y, 0.1, 0.1, (int)upperConnection, (int)rightConnection, (int)bottomConnection, (int)leftConnection, false, index, scale); piece.SetValue(Canvas.ZIndexProperty, 1000 + x * rows + y); piece.MouseLeftButtonUp += new MouseButtonEventHandler(Piece_MouseLeftButtonUp); piece.MouseRightButtonUp += new MouseButtonEventHandler(Piece_MouseRightButtonUp); piece.Rotate(piece, angle); var shadowPiece = new Piece(imageSource, x, y, 0.1, 0.1, (int)upperConnection, (int)rightConnection, (int)bottomConnection, (int)leftConnection, true, shadowPieces.Count(), scale); shadowPiece.SetValue(Canvas.ZIndexProperty, x * rows + y); shadowPiece.Rotate(piece, angle); pieces.Add(piece); shadowPieces.Add(shadowPiece); index++; } } } var tt = new TranslateTransform() { X = 20, Y = 20 }; foreach (var p in pieces) { Random random = new Random(); int i = random.Next(0, pnlPickUp.Children.Count); p.ScaleTransform.ScaleX = 1.0; p.ScaleTransform.ScaleY = 1.0; p.RenderTransform = tt; p.X = -1; p.Y = -1; p.IsSelected = false; pnlPickUp.Children.Insert(i, p); double angle = angles[rnd.Next(0, 4)]; p.Rotate(p, angle); shadowPieces[p.Index].Rotate(p, angle); } rectSelection.SetValue(Canvas.ZIndexProperty, 5000); rectSelection.StrokeDashArray = new DoubleCollection(new double[] { 4, 4, 4, 4 }); cnvPuzzle.Children.Add(rectSelection); }

这是加了WrappingStream垃圾回收的效果

这是未加WrappingStream垃圾回收的效果,

赞(0)
版权声明:本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
文章名称:.net中的托管资源与非托管资源,如何避免内存泄露
文章链接:https://www.jmwz.net/1340.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
分享到: 更多 (0)