续《JAVA编程思想》学习备忘(第155页:Initialization & Cleanup)-3
The meaning of static
静态的含义
With the this keyword in mind,you can more fully understand what it means to make a method static.It means that there is no this for that particular method.You cannot call non-static methods from inside static methods(although the reverse is possible),and you can call a static method for the class itself,without any object.In fact,that's primaryly what a static method is for.It's as if you're creating the equivalent of a global method.However,global methods are not permitted in Java,and putting the static method inside a class allows it access to other static methods and to static fields.
Some people argue that static methods are not object-oriented,since they do have the semantics of a global method;with a static method,you don't send a message to an object,since there's no this.This is probably a fair argument,rethink your strategy.However,statics are pragmatic,and there are times when you genuinely need them,so whether or not they are "proper OOP"should be left to the theoreticians.
Cleanup:finalization and garbage collection
When the garbage collector is ready to release the storage used for your object,it will first call finalize(),and only on the next garbage-collection pass will it reclaim the object's memory.
You must perform cleanup
Remember that neither garbage collection nor finalization is guaranteed.If the JVM isn't close to running out of memory,then it might not waste time recovering memory through garbage collection.
The termination condition
Here's an simple example of how you might use it:
class Book{
boolean checkedOut = false;
Book(boolean checkOut){
checkedOut = checkOut;
}
void checkIn(){
checkedOut = false;
}
protected void finalize(){
if(checkedOut)
System.out.println("Error:checked out");
}
}
public class TerminationCondition{
public static void mian(String[] args){
Book novel = new Book(true);
// Proper cleanup:
novel.checkIn();
// Drop the reference, forget to clean up:
new Book(true);
// Force garbage collection & finalization:
System.gc();
}
}
输出:Error: checked out
运行垃圾回收器:System.gc()
How a garbage collector works
(以下为选择性地原文意译,此内容没有完全理解,待以后完善。)
垃圾回收器会影响到堆中对象创建的速度。
当在堆中创建对象时,Java 更像是一个一直向前移动的传送带,这意味着对象开辟存储空间非常地快,“堆指针”仅简单地向前移动到处女地。
你可能观察到,事实上堆并不像传送带……最终地,在你创建了足够的对象后,将会内存越界,这时招术将是垃圾回收器的进入,这样它将压堆中所有的对象。
知晓Java垃圾回收器,有助于了解别的系统中垃圾回收器是如何计划性地工作的。一个简单而慢速有垃圾回收技术叫做 reference counting 。……但它似乎没在任何JVM中使用来实现。
在较快的方案中,垃圾回收不是基于 reference counting 。代此而来的是基于任何非死亡的对象终将追述自无论栈中或静态存储域中活着的参照体的想法,此链可能穿越几层对象。这样,如果你开始于栈中和静态存储域中并且穿越所有的参照体,你会发现所有的活着的对象,对于你发现的每个参照体,你必须追寻入它所指的对象,并且随此对象的参照体追寻入它们所指向对象……,直到你穿越过整个用栈上或静态存储域中的参照体初创的Web 。你穿越的每个对象必须仍然生存着。注意那儿没有分离的自指群问题,这些简单地没有发现,而所以自动地废弃。
这里接近的阐述为,JVM 用一个适合的垃圾回收方案,而且对于它锁定的生存着的对象做什么取决于当前被使用的变体。变体之一是 stop-and-copy 。这意味着有理由地将会显而易见的是程序首先停止(这不是一个收集方案的后台),接着每个生存着的对象被从一个堆拷贝到另一个堆,甩掉所有的垃圾。有条件地,当对象被拷贝进入新堆,它们被包装着尾(接)到尾,就这样紧压新堆(并且就如前述允许新存储空间简单地从尾部剥离掉)。
当然,当一个对象从一个地方移动到另一个地方,所有指向此对象的参照体必须改变。堆或静态存储域中参照体可被直接改变,但是那里可能存在别的指向此对象的参照体将在随后的“walk”期间遭遇到。当它们被发现时这些处于混杂状(你可以想象如一个表将旧地址映射到新表)。
有两个问题让这些被称作“拷贝收集器”的无效。第一个是你有两个堆而且你前后晃动所有两个堆之间的内存的注意,保留两次以上你实际所需的内存。一些 JVM 因此在需要时用磁记忆块开辟堆并且简单地从一个磁记忆块拷贝到另一个磁记忆块。
第二个是拷贝进程自身问题。一旦你的程序变得稳固,它可能产生很少或没有垃圾。不顾这些,“拷贝收集器”仍将拷贝所有内存从一个地方到另一个地方,这太浪费了。为避免这样,一些 JVM 侦测到没有新的垃圾产生就转到一个不同的(处理)方案(此为“适合的”部分)。这个不同的方案被称作“mark-and-sweep”(标注与清除),而它的 sun 公司的 JVM 的早期版本被使用至今。对普通的应用,mark-and-sweep 为中速,但当你得知你产生很少或没有垃圾时,它很快。
Mark-and-sweep 随从栈和静态存储启动的同样逻辑,且穿越所有的参照体来发现活着的对象。无论如何,每次找到一个活着的对象,这个对象就被标注并设置一个记号,但这个对象还没被收集。只有当标注进程完结时才会发生清除。在清除期间,死掉的对象被释放掉。无论如何,没有拷贝发生,这样如果收集器选择压一个碎掉的堆,它用滑移周围对象来做此。
“Stop-and-copy”涉此想法,即在后台此类垃圾回收器不会做;代替此,当垃圾回收发生时程序被停止。在 Sun 公司文字描述中,你将发现许多垃圾回收作为一优先级别低的后台进程的参考,但它提示出此方式垃圾回收没被实现在早期 Sun 公司的 JVM。代替此的是,当内存变慢时Sun 公司的垃圾回收器终止程序。“Mark-and-sweep”也要求终止程序。
如前所提,内存被大块开辟。如果你开辟一个大的对象(空间),它得到它自有的块。严格的 stop-and-copy 要求拷贝每一个活着的对象从源堆到一个新堆在你能释放掉旧有的之前。转换至大量的内存。随大块,垃圾回收能典型地拷贝对象到死去的块当被收集。每一个块拥有一个 generation count 来保留对象是否生存的痕迹。通常情况下,只有块被创建自从最后的垃圾回收被压;所有其它块得到它们的 generation count 弹出当它们在某处被参照。这掌控了大量短期生存的当前对象的通常情况。周期地,执行一个全面的清除,大的对象仍不拷贝(它们仅使其 generation count 弹出),而块包含小的对象被拷贝和压。JVM 监视垃圾回收效率并且当它变得浪费时间,因为所有的对象是长存之时,它转换到 mark-and-sweep 。相似地,JVM 保留 mark-and-sweep 如何成功的痕迹,并且如果堆开始变得破碎,它转换到 stop-and-copy 。这就是那“合适的”部分的来由,这样你一句结束:“Adaptive generational stop-and-copy mark-and-sweep”。
在 JVM 中有一系列有条件的加速。一个特别重要的例子参与了加载器操作而叫做一个 just-in-time(JIT)编译器。一个JIT编译器部分地或者全面地转换一个程序到本地机器编码,这样它不需要被JVM中断,而这样就运行更快了。当一个类必须被加载(典型地,你要首次创建类的一个对象时),.class 文件被定位,而类的字节码被带入内存。在此点上,一个接近的(方法)是简单地用JIT编译所有的代码,但这有两个不利之处:它花费较多的时间,它混杂于整个程序生存期间,能累积;而且增加执行体(字节码比起扩展JIT代码较多的压缩),而且这可能引发事件,这绝对地使程序降速。一个替代的接近(方法)是 lazy evaluation ,它的意思是代码不被 JIT 编译除非必须。这样,代码比从不被 JIT 编译更可能从不被执行。这种当前 JDK 中 Java 的热点技术被每次执行时增涨地优化一小段代码带来一个相似的接近(方法),这样越多的代码被执行,速度越快。
<待续>