谈谈.net对象生命周期(垃圾回收)

不用程序员操心的堆 —托管堆

  程序在计算机上跑着,就难免会占用内存资源来存储在程序运行过程中的数据,我们按照内存资源的存取方式将内存划分为堆内存和栈内存

  栈内存,通常使用的场景是:对存取速度要求较高且数据量不大。

  典型的栈内存使用的例子就是函数栈,每一个函数被调用时都会被分配一块内存,这块内存被称为栈内存,以先进后出的方式存取数据,在函数执行过程中不断往函数栈中压入(PUSH)数据(值类型数据:int、float、对象的引用...),函数执行完后又将函数栈中的数据逐个弹出(POP),由于是以操作栈的形式来存取,所以访问速度快。

  堆内存,从字面意思上理解就好像是仓库里面可以存一堆破烂,你若是需要存点什么东西就尽管往里面一扔,仓库里有的是空间。事实确实也是如此,堆内存中可以存放大规格的数据(比如对象资源),这些数据是不适合存放在栈中的,因为栈空间的容量有限,这就是堆内存相对于栈内存的好处:容量大。但是它的缺点也是显而易见的,那就是存取堆内存的数据相较于存取栈内存是非常慢的,试想一下,让你在仓库里的一堆破烂里去找你想要的东西是什么感觉。

  从内存分配方式上看,堆内存不同于栈内存,函数栈是在每一个函数被执行的时候被自动分配并且函数执行完成后自动回收,而如果你想使用堆内存,就得自己动手丰衣足食。

  所以你会看到c语言程序员会这样去使用堆内存:

int *p = (int*)malloc(sizeof(int)); //在堆内存中申请一块字节数为int字节数的堆内存,并返回指向该内存区域的指针
*p = 10;
free(p); //释放堆内存资源

  你还会看见c++程序员这样写:

Car* bmw = new Car(); //创建一个Car类对象,在堆内存中存放对象数据,并返回指向对象资源的指针
delete bmw; //释放堆内存资源

  当然,没有接触过c/c++的小伙伴也不用惊慌,上面只不过是想让你知道在c/c++语言中,程序员要是想使用堆内存,那就必须显式地编写分配和释放堆内存资源的代码。

  有人问:使用完堆内存资源后没有手动释放它会有什么后果吗?

  答案是:由于堆内存资源使用者未及时释放内存会导致内存无法再次使用,从而造成内存资源的泄漏(浪费)。

  就在这个时候,c#程序员笑了,只见他的手指非常轻盈优雅地在屏幕上敲出了下面这行代码:

 Car bmw = new Car();

  一旁围观的c程序员和c++程序员惊呆了,他们不知道自己在敲代码的时候有没有像这样轻松过。c++程序员用手抚摸着他那锃光瓦亮的额头,突然眼睛里闪着光,喊道:“你还没有释放堆内存的资源呢,你这样是很危险的,会内存泄漏的,快,把释放堆内存的代码写上!”

  c#程序员似乎并不为所动,舒舒服服地靠在椅子上,用余光瞟了c++程序员一眼,说:“不用慌,不用慌,这个对象在托管堆上放的好好的呢,不用我操心”,于是,c#程序员便娓娓道来(呼呼大睡)...

  在.NET的世界,使用new关键字创建一个对象,首先对象资源被分配在托管堆中,然后new会返回一个指向堆上对象的引用,而不是真正的对象本身。如果在方法作用域中将引用变量声明为本地变量,这个引用变量保存在栈内,以供应用程序以后使用。

 

  托管堆,顾名思义,就是托给别人管的堆,那么是谁在管理着这个堆上的对象资源呢?

  答案是:CLR(Common Lanauage Runtime),对象的实例化结束以后,GC(垃圾回收器)将会在对象不再需要时将其销毁。

  也就是说,通过允许垃圾收集器负责销毁对象,内存管理的麻烦就都交给CLR了,万事大吉。

 

  看似问题好像都已水落石出,无非就是将堆内存资源回收交给了CLR去承担。难道你就不想知道的更多一点?比如接着而来的问题:

  1、垃圾回收器如何判断一个对象什么时候不再需要?

  2、垃圾回收器又在什么时候会执行垃圾清理的操作?

  别急,带着问题慢慢往下看。

CIL的new指令— 垃圾回收的触发者

  c#中的new关键字最终会被编译器翻译成CIL的newobj指令,让我们仔细查看一下CIL newobj指令的作用。

  首先,需要明白托管堆不仅仅是一个可由CLR访问的随机内存块。.NET垃圾回收器是堆的“清洁工”,出于优化的目的它会压缩空闲的内存块(当需要时)。为了辅助压缩,托管堆会维护一个指针(通常被叫做下一个对象指针或者是新对象指针),这个指针用来标识下一个对象在堆中分配的地址。

  此外,newobj指令通知CLR来执行下列的核心任务:

  (1)计算要分配的对象所需的全部内存(包括这个类型的数据成员和类型的基类所需的内存)。

  (2)检查托管堆来确保有足够的空间来放置所申请的对象。如果有足够的空间,会调用这个类型的构造函数,构造函数会返回一个指向内存中这个新对象的引用,这个新对象的地址刚好就是下一个对象指针上一次所指向的位置。

  (3)最后,在把引用返回给调用者之前,让下一个对象指针指向托管堆中下一个可用的位置。

  下面的图解释了在托管堆上分配对象的细节。

  在c#中分配对象是一个很频繁的操作,照这样下去托管堆上的空间迟早会被挥霍完,所以,重点来了,如果CLR 发现托管堆没有足够空间分配请求的类型时,它会执行一次垃圾回收来释放内存

  当执行垃圾回收时,垃圾收集器临时挂起当前进程中的所有的活动线程来保证在回收过程中应用程序不会访问到堆。(一个线程是一个正在执行的程序中的执行路径)。一旦垃圾回收完成,挂起的线程又可以继续执行了。还好,.NET 垃圾回收器是高度优化过的,所以用户很少能察觉到应用程序中的短暂中断。

  通过对CIL的new指令作用的解读,我们知道了:如果托管堆没有足够的空间分配一个请求的对象,则会执行一次垃圾回收。

(讲到这里c#程序员停了下来,喝了口保温杯里的枸杞红枣大补茶🍵,清了清嗓子,继续开始解惑...)

应用程序根的作用 — 区分不可到达的对象

  现在让我们来讨论一下垃圾回收器怎样确定什么时候“不再需要”一个对象。为了理解细节,你需要知道应用程序根的概念。

  简单来说,一个根是一个引用,这个引用指向堆上面的一个对象的。严格来说,一个根可以有以下几种情况:

  (1) 指向全局对象的引用(尽管C#不支持,但CIL代码允许分配全局对象)

  (2)指向任何静态对象

  (3)指向一个应用程序代码中的局部对象

  (4)指向传入到一个函数中的对象参数

  (5)指向等待被终结(finalized)的对象

  (6)任何一个指向对象的CPU寄存器

  在一次垃圾回收的过程中,运行环境会检查托管堆上面的对象是否仍然是从应用程序根可到达的。为了检查可达,CLR会建立一个代表堆上每个可达对象的图。对象图用来记录所有可达的对象。同时,注意垃圾回收器绝不会在图上标记一个对象两次,因此避免了烦人的循环引用。

  假设托管堆上有名字为A,B,C,D,E,F和G的对象集合。在一次垃圾回收过程中,会检查这些对象(同时包括这些对象可能包含的内部对象引用)是否是根可达的。一旦图被建立起来,不可达的对象(在此是对象C和F)被标记为垃圾。

  下图是上述场景的一个可能的对象图(你可以把箭头读作依赖或者需要,例如"E依赖于G,间接依赖于B,“A不依赖任何对象”等)。

(创建的对象图是用来决定哪些对象是应用程序根可达的。)

  一旦一个对象已经被标记为终结(此例子中是C和F--在图中没有他俩),它在内存中就被清理掉了。在此时,堆上的剩余内存空间被压缩,这会导致CLR修改活动的应用程序根集合(和对应的指针)来指向正确的内存位置(这个操作是自动透明的)。最后,调整下一个对象指针来指向下一个可用的内存位置。

  下图阐明了清除和压缩堆的过程。

  到这里,通过对应用程序根的作用的理解,我们知道了如何知道一个对象是“不再需要”的。通俗点来说就是,这个对象在应用程序中已经无需被访问了,成为了一座“孤岛”,自然也就不再需要它了。

  (为了让c++程序员能更加理解. net垃圾回收的奥妙,c#程序员继续滔滔不绝…)

理解对象的代 — 垃圾回收过程的优化

  在尝试找到不可达的对象时,CLR并不是检查托管堆上的每个对象。很明显,这样做会消耗大量时间,尤其在大型(例如现实中)程序中。

  为了帮助优化这个过程,堆上的每个对象被分配到一个特殊的"代”。代这个概念背后的想法很简单:对象在堆上存活的时间越长,接下来它继续存在的可能性也就越大,即较旧的对象生存期长,较新的对象生存期短。例如,实现Main()的对象一直在内存中,直到程序结束。相反,最近才被放到堆中的对象(例如在一个函数范围里分配的对象)很可能很快就不可达。

  在堆上的每个对象属于以下的某一个代:

  Generation 0: 标识一个最近分配的还没有被标记为回收的对象

  Generation 1: 标识一个经历了一次垃圾回收而存活下来的对象(例如,他被标记为回收,但由于堆空间够用而没有被清除掉)

  Generation 2:标识一个经历了不止一轮垃圾回收而存活下来的对象。

  垃圾回收器首先会检查generation 0的所有对象。如果标记并清理这些对象(译者注:因为新对象的生存期往往较短,并且期望在执行回收时,应用程序不再使用第 0 级托管堆中的许多对象)后产生了足够使用的内存空间,任何存活下来的对象就被提升到Generation 1。为了理解一个对象的代如何影响回收的过程,可以查看下图。下图解释了generation 0中一次垃圾回收后,存活的对象被提升的过程。  

(generation 0 中的存活对象被提升到generation 1)

  如果所有的generation 0对象都被检查了,但是产生的内存空间仍然不够用,就检查一遍generation 1中的所有对象的可达性并回收。存活下来的generation 1对象被提升到generation 2。如果垃圾回收器仍然需要额外的内存,generation 2的对象就经历检查并被回收。此时,如果一个generation 2的对象存活下来,它仍然是一个generation 2的对象。

  其实通过对象的代的设计是想达到这么一个效果:新对象(比如局部变量)会被很快回收,而老一些的对象(如一个应用程序对象)不会被经常骚扰。

  说到底,对象代的设计就是为了优化垃圾回收的过程。  

  “我还有最后一个问题”,c++程序员按耐不住心里一直的疑惑,说到:“你说了这么多都是再讲托管资源,难道.net中就没有非托管资源吗?. net又是怎么对非托管资源进行资源释放的呢?”。

  "这个问题问的好!",c#程序员大笑,于是接着又开始解惑(吹B)…

构建可终结对象 —非托管资源处理第一式

  以一名c#开发者的直觉告诉你,大多数的c#类都不需要显式的清理逻辑。原因很简单:如果类型使用了其他托管对象,一切都最终会被垃圾回收。

  问:那在什么时候需要显式地清理呢?

  答案是:在你使用非托管资源时(例如原始的操作系统文件句柄、原始的非托管数据连接或其他非托管资源),才可能需要设计一个在用完后清理自身垃圾的类。

  比如说下面这个类:

//数据库上下文类
  public class SqlDbContext
  {
    //...(其他被引用的对象实例)

    //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放)
    SqlConnection sqlConnection = new SqlConnection("..."); 

  }

  现在问题来了,我们要在适当的时机调用数据库连接类对象释放资源的方法(SqlConnection类对象使用完后需要调用Dispose()方法释放资源)。这个适当的时机当然就是对象在被CLR进行垃圾回收的过程中,所以问题又来到了,有没有一个方法是在这个时机被调用,而且是可以被扩展的呢?

  是的,我们可以利用. NET的基类System.Object中定义的名为Finalize()的虚方法,也叫作终结器方法,它是这样的:

  

  看到这当然会很奇怪,不是说有Finalize()方法,在哪,逗我?莫惊讶,其实这里的 ~Object() 就是Finalize(),只是一个语法糖罢了。

  Finalize()的调用将(最终)发生在一次"自然的"垃圾回收或用程序通过GC.Collect()强制回收的过程中,所以这样看来,终结器方法就是让类对象释放内部非托管资源的地方。nice,现在我们可以像这样来编写清理非托管资源的代码:

//数据库上下文类
    public class SqlDbContext
    {
      //...(其他被引用的对象实例)

      //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      ~SqlDbContext()
      {
        //这里清除非托管资源
        this.sqlConnection.Dispose();
      }

    }

  这样被构建的对象被叫做可终结对象。

  有关于终结过程的细节,在《C#与.NET4高级程序设计(第5版)》书中是这样描述的:

  从以上的内容我们得知:通过Finalize()来清除非托管资源的时机只能是在.NET对象被垃圾回收的过程中,而且终结过程是一个消耗不小的动作。

  问题又来了:很多非托管资源都非常宝贵(如数据库和文件句柄),所以这些资源应该在使用完后尽快地被清除,而不能依靠垃圾回收的发生,那么这些资源应该以怎样的形式被显示地释放呢?

 

构建可处置对象—非托管资源处理第二式

除了重写 Finalize() 之外,类还可以实现 IDisposable 接口,它定义了一个名为 Dispose() 的方法:

 public interface IDisposable
  {
    void Dispose();
  }

  它的使用方法就是:在类的Dispose()方法中编写非托管资源的释放的代码,程序员可以在这个对象不再需要的时候手动调用对象的Dispose()方法来达到及时释放非托管资源的目的。

  于是你可以像这样来编写类:

//数据库上下文类
    public class SqlDbContext:IDisposable
    {
      //...(其他被引用的对象实例)

      //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      public void Dispose()
      {
        //这里清除非托管资源
        this.sqlConnection.Dispose();
      }
    }

  采用这种方式来释放非托管资源的类被称作为可处置对象。

  在这里还要补充一点,C#提供了一个语法糖来简化调用Dispose()操作,如下:

SqlDbContext context = new SqlDbContext();

  try
  {
    //在此作用域内使用SqlDbContext类对象context
  }
  finally
  {
    //确保使用完后调用Dispose()方法
    context.Dispose();
  }

  上面这段代码等同于下面这段代码:

using (SqlDbContext context = new SqlDbContext())
  {
    //在此作用域内使用SqlDbContext类对象context
  }

  c++程序员说:“你这还不是要自己手动调用,如果我忘记调用 Dispose() 那岂不是一切都玩完!”

  c#程序员冷笑一声,“非也,非也,我来传授你最后一招吧!”

非托管资源最强模式 — 双剑合璧

 人非圣贤,孰能无过。程序员也会有失手的时候,比如,忘记调用 Dispose() 方法...

  这个时候就必须设计一个万无一失的方法,达到一个目的:就是不管有没有手动调用Dispose(),非托管资源最终都应该被妥妥地释放掉。为了解决这个问题,我们可以如下去定义一个可处置对象类:

//数据库上下文类
    public class SqlDbContext:IDisposable
    {
      //...(其他被引用的对象实例)

      //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放)
      SqlConnection sqlConnection = new SqlConnection("..."); 

      ~SqlDbContext()
      {
        //这里清除非托管资源
        this.sqlConnection.Dispose();
      }

      public void Dispose()
      {
        //这里清除非托管资源
        this.sqlConnection.Dispose();

        //跳过终结过程
        GC.SuppressFinalize(this);
      }

  可以看到,这个类中即有终结方法的重写也有Dispose()方法,这样就能保证:程序员若忘记调用Dispose()方法释放非托管资源,那么对象就会在垃圾回收的过程中调用终结方法来释放非托管资源;若程序员调用了Dispose()方法,那么 GC.SuppressFinalize(this) 会保证在垃圾回收过程中不再会调用对象的终结方法,避免不必要的资源开销。可谓“双剑合璧”,保万无一失。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 验证一个ASP.NET应用程序和页面的生命周期的实现代码

    如果我们能更好地掌握这样一个过程,那么对单个ASP.NET Page的生命周期也能更好地了解: 下面介绍如何编写一个简单的ASP.NET 页面和一个简单的HttpModule,对MSDN里提到的ASP.NET的生命周期进行验证 1. 首先使用Visual Studio 2010建立一个空的ASP.NET网站 (ASP.NET 4.0) 2. 添加一个Default.aspx,添加三个ASP.NET控件,分别为TextBox,Button和Validator: 复制代码 代码如下: <form i

  • Asp.Net Core中服务的生命周期选项区别与用法详解

    前言 最近在做一个小的Demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,寻找一番,发现是配置依赖注入服务时,对于服务的生命周期没有配置得当导致,特此做一次实验来认识三者之间(甚至是四者之间的用法及区别). 本文demo地址(具体见WebApi控制器中):https://gitee.com/530521314/koInstance.git (本地下载)  一.服务的生命周期 在Asp.Net Core

  • ASP.NET Web页生命周期和执行的方法介绍

    以上所列的阶段中有些在页面级是不可见的,并且仅对服务器控件的编写者和要创建从 Page 导出的类的开发人员有意义.Init.Load.PreRender.Unload,再加上由嵌入式控件定义的所有回发事件,就构成了向外发送页面的各个阶段标记. 执行的各个阶段 页面生命周期中的第一个阶段是初始化.这个阶段的标志是 Init 事件.在成功创建页面的控件树后,将对应用程序触发此事件.换句话说,当 Init 事件发生时,.aspx 源文件中静态声明的所有控件都已实例化并采用各自的默认值.控件可以截取 I

  • 详解ASP.NET页面生命周期事件

    下面是ASP.NET页面初始的过程:1. Page_Init();2. Load ViewState;3. Load Postback data;4. Page_Load();5. Handle control events;6. Page_PreRender();7. Page_Render();8. Unload event;9. Dispose method called; 下面对其中的一些过程作下描述:1. Page_Init();这个过程主要是初始化控件,每次页面载入执行这个初始过程,

  • ASP.NET 页生命周期概述(小结)

    ASP.NET 页运行时,此页将经历一个生命周期,在生命周期中将执行一系列处理步骤.这些步骤包括初始化.实例化控件.还原和维护状态.运行事件处理程序代码以及进行呈现.了解页的生命周期非常重要,这样就能在合适的生命周期阶段编写代码,以达到预期效果.此外,如果开发自定义控件,则必须熟悉页生命周期,从而正确地初始化控件,使用视图状态数据填充控件属性以及运行所有控件行为逻辑.(控件的生命周期基于页的生命周期,但是页引发的控件事件比单独的 ASP.NET 页中可用的事件多.) 常规页生命周期阶段 一般来说

  • 详解ASP.NET页面生命周期

    ASP.NET页面运行时候,页面将经历一个生命周期,在生命周期中将执行一系列的处理步骤.包括初始化.实例化控件.还原和维护状态.运行时间处理程序代码以及进行呈现.熟悉页面生命周期非常重要,这样我们才能在生命周期的合适阶段编写代码.如果我们能在写代码的时候想着我们现在是在做生命周期的哪一步那将是非常好的. 几个代表性的问题 在开始的时候我们先思考几个问题,看看我们在描述完页面生命周期的时候,能不能回答上这几个问题 1.为什么在服务器端能通过this.textbox1.Text获取到用户提交过来的数

  • 基于asp.net MVC 应用程序的生命周期(详解)

    首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的生命周期,从一个控件到另一个控件是怎样被处理的.我们还会详细介绍一下整个请求的生命周期中,用到的相关组件.因为在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller和action方法之间做相关的处理,对于真正内在的运行机制可能不是很了解.其实

  • asp.net页面生命周期详解

    Asp.net是微软.Net战略的一个组成部分.它相对以前的Asp有了很大的发展,引入了许多的新机制.本文就Asp.net页面的生命周期向大家做一个初步的介绍,以期能起到指导大家更好.更灵活地操纵Asp.net的作用.当一个获取网页的请求(可能是通过用户提交完成的,也可能是通过超链接完成的)被发送到Web服务器后,这个页面就会接着运行从创建到处理完成的一系列事件.在我们试图建立Asp.net页面的时候,这个执行周期是不必去考虑的,那样只会自讨苦吃.然而,如果被正确的操纵,一个页面的执行周期将是一

  • ASP.NET服务器控件的生命周期分析

    本文实例分析了ASP.NET服务器控件的生命周期.分享给大家供大家参考.具体如下: (1)初始化----在此阶段中,主要完成两项工作:一.初始化在传入Web请求生命周期内所需的设置:二.跟踪视图状态.首先,页面框架通过默认方式引发Init事件,并调用OnInit()方法,控件开发人员可以重写该方法为控件提供初始化逻辑.此后,页面框架将调用TrackViewState方法来跟踪视图状态.需要注意的是:多数情况下,Control基类提供的TrackViewState方法实现已经足够了.只有在控件定义

  • 谈谈.net对象生命周期(垃圾回收)

    不用程序员操心的堆 -托管堆 程序在计算机上跑着,就难免会占用内存资源来存储在程序运行过程中的数据,我们按照内存资源的存取方式将内存划分为堆内存和栈内存. 栈内存,通常使用的场景是:对存取速度要求较高且数据量不大. 典型的栈内存使用的例子就是函数栈,每一个函数被调用时都会被分配一块内存,这块内存被称为栈内存,以先进后出的方式存取数据,在函数执行过程中不断往函数栈中压入(PUSH)数据(值类型数据:int.float.对象的引用...),函数执行完后又将函数栈中的数据逐个弹出(POP),由于是以操

  • Java 详解垃圾回收与对象生命周期

    Java 垃圾回收与对象生命周期详解 Java中的垃圾回收与对象生命周期 1. 垃圾回收 垃圾回收是Java程序设计中内存管理的核心概念,JVM的内存管理机制被称为垃圾回收机制. 一个对象创建后被放置在JVM的堆内存中,当永远不再引用这个对象时,它将被JVM在堆内存中回收.被创建的对象不能再生,同时也没有办法通过程序语句释放它们.即当对象在JVM运行空间中无法通过根集合到达(找到)时,这个对象被称为垃圾对象.根集合是由类中的静态引用域与本地引用域组成的.JVM通过根集合索引对象. 在做Java应

  • CORBA对象生命周期

    我们知道,POA规范定义CORBA对象为具有标识.接口和实现的抽象实体.从客户机的角度来看,对象表示为对象引用,对象引用封装了对象接口类型和标识,并包含足够的信息来定位对象的实现.但从服务器的角度来看又怎样呢? 1.伺服对象 POA规范引入了伺服对象(servant)的概念,使抽象的CORBA对象能和实现该对象功能的具体编程语言实体彻底分离.这样从服务器的角度来看, CORBA对象是作为伺服对象实现的.要记住CORBA是与编程语言独立的体系结构.伺服对象可实现为C++或Java类,也可以实现为一

  • python对象销毁实例(垃圾回收)

    我就废话不多说了,直接上代码吧! '''python对象销毁(垃圾回收)''' class Point: 'info class' def __init__(self,x=0,y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print(class_name, '销毁') pt1 = Point() pt2 = pt1 pt3 = pt2 print(id(pt1),id(pt2

  • CORBA对象生命周期之实现和内存管理

    根据伺服对象的状态把它们进行分类后,现在基于内存管理来定义Corba对象的分类.显然,这个讨论和伺服对象的生命周期密切相关.这里侧重于分类,所以只是简单地讨论一下内存管理问题,并在下面引入一通用管理模式. 1. 静态Corba对象 静态对象是在系统整个生命时期中存在的Corba对象.通常,这些对象是组件入口点例如, Corba命名服务必须提供一根命名上下文,用来创建新的命名层次.这个根命名上下文可归类为静态的,因为它始终存在.从内存管理的角度来看,静态对象的实现是很简单的.通常,静态对象可通过在

  • 通过实例解析spring对象生命周期

    1.生命周期-@Bean指定初始化和销毁方法 配置时指定初始化及销毁方法: Bean中提供对应的初始化及销毁方法: package com.atguigu.bean; import org.springframework.stereotype.Component; @Component public class Car { public Car(){ System.out.println("car constructor..."); } public void init(){ Syst

  • Hibernate持久化对象生命周期原理解析

    三态的基本概念 1, 临时状态(Transient):也叫自由态,只存在于内存中,而在数据库中没有相应数据.用new创建的对象,它没有持久化,没有处于Session中,处于此状态的对象叫临时对象: 2, 持久化状态(Persistent):与session关联并且在数据库中有相应数据.已经持久化,加入到了Session缓存中.如通过hibernate语句保存的对象.处于此状态的对象叫持久对象: 3, 游离状态(Detached):持久化对象脱离了Session的对象.如Session缓存被清空的

  • Python对象的生命周期源码学习

    目录 思考: 1 C API 2 对象的创建 2.1 两种创建对象的方式 2.2 由类型对象创建实例对象 3 对象的多态性 4 对象的行为 5 引用计数 思考: 当我们输入这个语句的时候,Python内部是如何去创建这个对象的? a = 1.0 对象使用完毕,销毁的时机又是怎么确定的呢? 下面,我们以一个基本类型float为例,来分析对象从创建到销毁这整个生命周期中的行为. 1 C API Python是用C写的,对外提供了API,让用户可以从C环境中与其交互,并且Python内部也大量使用了这

  • Microsoft .Net Remoting系列教程之二:Marshal、Disconnect与生命周期以及跟踪服务

    一.远程对象的激活 在Remoting中有三种激活方式,一般的实现是通过RemotingServices类的静态方法来完成.工作过程事实上是将该远程对象注册到通道中.由于Remoting没有提供与之对应的Unregister方法来注销远程对象,所以如果需要注册/注销指定对象,微软推荐使用Marshal(一般译为编组)和Disconnect配对使用.在<Net Remoting基础篇>中我已经谈到:Marshal()方法是将MarshalByRefObject类对象转化为ObjRef类对象,这个

  • 老生常谈Java虚拟机垃圾回收机制(必看篇)

    在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中.如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致OOM. JVM提供了一种垃圾回收机制,简称GC机制.通过GC机制,能够在运行过程中将堆中的垃圾对象不断回收,从而保证程序的正常运行. 垃圾对象的判定 我们都知道,所谓"垃圾"对象,就是指我们在程序的运行过程中不再有用的对象,即不再存活的对象.那么怎么来判断堆中的对象是"垃圾&q

随机推荐