1.Java静态绑定和动态绑定
1.1静态绑定:
静态绑定就是所谓的程序编译时绑定,java中的变量都是静态绑定的,方法只有static和final(所有private默认是final的,子类不可能修改父类的私有方法)是静态绑定的。编译时已经确切知道程序所要执行的是哪一个类的哪一个方法,不存在
重载等问题。
例如:static、private修饰的方法或者变量,对于方法的调用或者变量的使用是不存在歧义的,所以在编译时即可确定。
1.2动态绑定:
动态绑定运行时绑定,编译时不知道程序所引用的对象只有在程序执行时才能确定。
例如:Parent p=new Child(),父类中的public方法,如果在子类中进行了重载,声明的父类引用实际上绑定的是子类的对象,在使用父类中被重载的方法时,其实指向的是子类中的方法,这一点只能在程序被运行到才能确定。
1.3 向上转型、向下转型:
?Java向上转型是安全的,而向下转型是不安全的。
?对于向上转型(Java多态的体现),给父类的引用绑定上子类的对象,父类中的非private属性以及方法在子类中都得到了继承。在向上转型的过程中并不会丢失父类的属性(static/private的属性或者方法在声明对父类的引用时已经确定),所以这个过程是安全的。当程序执行时,父类被重载的方法会动态绑定为子类的方法(若子类中未进行重载,执行的将是父类的方法)。由于向上转型声明的是对父类型的引用,所以在向上转型会丢失掉子类定义的非重载的方法(其实是隐藏了,可以通过instanceof进行向下转型的检查,进行cast来重新获取对子类特有方法的调用)。
?对于向下转型是不安全的,这一点很显然,声明子类的引用却动态绑定上了父类对象(因为是动态绑定,所以这一
类型转换错误只会在执行时
发现),子类中定义的变量或函数,父类中是不可能拥有的。但是对于向上转型的逆过程这一
安全性问题又可以得到解决,因为堆里保存的对象本身就是子类对象。
Parent p=new Child();
if(a instanceOf Child) (Child) p;//向下转型安全
注:Java是oop语言,不同于C++(为了兼容C),java的所有类都继承了Object。Java中的某些容器存储的对象也是通用类型Object,由于Java继承的单根结构,所以容器可以存储所有类型的对象(向上转型)。但是从容器中取出对象时需要进行向下转型(除非想取到Object对象),这个过程就需要用到向下转型,而这里存在了不安全因素,程序会进行向下转型的检查,会造成额外开销,所以养成良好的编程
习惯,在使用容器时指定容器存储的对象类型。
2.GC垃圾回收
2.1对象存储
Java不同于C++,C++对象通常需要通过调用
析构函数进行对象的释放,这与C++采用的
堆栈方式存储对象机制相关(当然C++还提供了其他的方式),对象的存储与释放对应堆栈的push/pop,采用这种方式对象的创建以及释放会很快速,然而却
限制了程序的灵活性,因为对象
内存分配得在程序执行前就得确定,并且如果忽略了对象的释放常常会导致内存泄漏。
Java的对象存储是利用的堆机制,Java程序在编译时会
创建对象的引用(对象引用可以是堆栈存储),在程序执行时如果需要创建对象,则在程序所分配的堆中创建这样一个对象。Java堆类似于一个池,而且对象的存放是紧凑的(这样对于
内存分配以及是释放都有好处),当创建一个对象时,会根据当前的堆“指针”查找到堆中的空闲区域,然后向后分配给这个对象内存空间,分配后修改“指针”位置。从这个角度上讲Java对象的创建和C++的堆栈存储对象效率上并没什么区别,但是Java并不要求
程序员人为的完成对对象的释放,而堆的空间是有限的如何
合理的分配堆释放那些无用的对象就需要使用到Java的垃圾回收了。
2.2垃圾回收
2.2.1Java的GC是一种“停止——复制、标记——清理、分代、自适应”的垃圾回收。
2.2.2GC原理:
首先GC发生的时间,一般有两种一种是内存不足、另一种是空闲时,因为垃圾回收需要暂停程序执行,需要系统开销,所以不到一定时候不会进行垃圾回收。对于垃圾回收原理,当程序的heap空间不足时,gc发生,需要对那些不再被引用的“死”对象进行释放,进行垃圾回收首先需要跟踪对象的引用以确定哪些对象可以被释放哪些不可以被释放。
JVM会根据堆栈中存放的对象引用,到heap中查找响应的对象,因为对象的创建是在new的时候发生,我们无法事先知道一个对象的生命周期,但是我们可以找到对对象的所有引用,当对该对象的所有引用都结束后,对象其实就可以进行释放。对于对象内部引用对象的情况,只需要按照“引用—>对象—>引用…”的方式一直找下去即可完成对所有对象的跟踪。这样完成了对“垃圾”的查找。
2.2.3GC方式
完成对“垃圾”的查找,如何对“垃圾进行清理”?垃圾清理之前,程序需要被迫暂停,因为gc不仅仅完成的是对heap中无用对象的清理,同时也对对象的存储进行了整理。
2.2.3.1垃圾清理首先采用的方式是“停止——复制”方法:
将堆中所有的“活对象”复制到另外一个堆中,原有的堆内所有对象即可全部释放。对象被复制到另外的堆中,并且进行紧凑的排列,这样程序在创建新对象时依赖对指针查找空闲空间即可。但是这样的“停止——复制”方式带来一个问题,需要额外的多分配一倍的空间,而且来回的在内存页中切换复制需要耗费时间,有的JVM采用的
解决方法是给堆分配几块大的内存页,这样复制只需要在少数的几个内存页中进行。
2.2.3.2“标记——清扫”方法:
可以看出,“停止——复制”的处理办法其实完成的是将活对象复制保留,将整个堆内所有对象释放(无论对象是否被引用),然而对于垃圾较少的情况下这种方式很浪费。“标记——清扫”方法适用在程序执行稳定垃圾较少的情况下,比如在完成“停止——复制”之后,同样需要先标记出所有活对象,对于那些未被标记的对象(“垃圾”)进行空间的释放。(释放后是否进行整理?否则出现碎片?)
2.2.3.3分代:
Java的堆都是以大的“块”为单位,每个块都有响应的代数,如果块中某个对象被引用,则代数增加。在垃圾回收时,对于块中的大型对象GC不进行复制操作,而是增加块的代数并将块内的小型对象进行复制整理,在对块进行“标记——清扫”处理垃圾,如果发现大量的内存碎片,在进行“停止——复制”整理碎片。
2.2.3.4自适应:
GC的自适应体现在“停止——复制”、“标记——清扫”以及分代的垃圾处理方式的选取。
- 大小: 120.5 KB