7. final问题:
final使得被修饰的变量"不变",但是由于对象型变量的本质是"引用",使得"不变"也有了两种含义:引用本身的不变?,和引用指向的对象不变。? 引用本身的不变:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//编译期错误
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//编译期错误
引用指向的对象不变:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //编译通过
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //编译通过
可见,final只对引用的"值"(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的"值"相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。在举一个例子:
public class Name {
private String firstname;
private String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
public class Name {
private String firstname;
private String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
编写测试方法:
public static void main(String[] args) {
final Name name = new Name();
name.setFirstname("JIM");
name.setLastname("Green");
System.out.println(name.getFirstname()+" "+name.getLastname());
}
public static void main(String[] args) {
final Name name = new Name();
name.setFirstname("JIM");
name.setLastname("Green");
System.out.println(name.getFirstname()+" "+name.getLastname());
}
理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它"永远不变"。其实那是徒劳的。 Final还有一个值得注意的地方: 先看以下示例程序:
class Something {
final int i;
public void doSomething() {
System.out.println("i = " + i);
}
}
class Something {
final int i;
public void doSomething() {
System.out.println("i = " + i);
}
}
对于类变量,Java虚拟机会自动进行初始化。如果给出了初始值,则初始化为该初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。但是对于用final修饰的类变量,虚拟机不会为其赋予初值,必须在constructor (构造器)结束之前被赋予一个明确的值。可以修改为"final int i = 0;"。
8. 如何把程序写得更健壮:
1、尽早释放无用对象的引用。 好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。对于仍然有指针指向的实例,jvm就不会回收该资源,因为垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率;
2、定义字符串应该尽量使用 String str="hello"; 的形式 ,避免使用String str = new String("hello"); 的形式。因为要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
public Demo() {
s = "Initial Value";
}
}
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
3、我们的程序里不可避免大量使用字符串处理,避免使用String,应大量使用StringBuffer ,因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象,请看下列代码;
String s = "Hello";
s = s + " world!";
String s = "Hello";
s = s + " world!";
在这段代码中,s原先指向一个String对象,内容是 "Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。 通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为 String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
4、尽量少用静态变量 ,因为静态变量是全局的,GC不会回收的;
5、尽量避免在类的构造函数里创建、初始化大量的对象 ,防止在调用其自身类的构造器时造成不必要的内存资源浪费,尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。 以下是初始化不同类型的对象需要消耗的时间:
运算操作 示例 标准化时间
本地赋值 i = n 1.0
实例赋值 this.i = n 1.2
方法调用 Funct() 5.9
新建对象 New Object() 980
新建数组 New int[10] 3100
从表1可以看出,新建一个对象需要980个单位的时间,是本地赋值时间的980倍,是方法调用时间的166倍,而新建一个数组所花费的时间就更多了。
6、尽量在合适的场景下使用对象池技术 以提高系统性能,缩减缩减开销,但是要注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源,综合考虑应用运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。
7、大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理 ,然后解决一块释放一块的策略。
8、不要在经常调用的方法中创建对象 ,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。
9、一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。
10、尽量少用finalize函数 ,因为finalize()会加大GC的工作量,而GC相当于耗费系统的计算能力。
11、不要过滥使用哈希表 ,有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法
分享到:
相关推荐
【Java内存管理总结】 Java内存管理的核心是对象的分配与释放,主要分为两大部分:对象的分配和垃圾回收。在Java中,所有对象都在堆内存(Heap)中分配空间,而对象的释放则由垃圾回收机制(Garbage Collector,...
【JAVA 内存管理总结】 Java 的内存管理主要涉及对象的生命周期,包括对象的分配和释放。对象的分配主要是通过 `new` 关键字在堆内存(Heap)中完成,而对象的释放则由垃圾回收机制(Garbage Collector, GC)自动...
Java虚拟机内存管理总结 Java虚拟机(JVM)中的内存管理是指Java语言中对象的分配和释放问题。Java中的内存管理可以分为两部分:对象的分配和释放。 对象的分配是由程序完成的,程序员需要通过关键字new为每个对象...
通过对Java内存管理机制的深入分析,我们可以了解到Java如何高效地管理和利用内存资源。理解这些机制对于优化Java应用程序的性能至关重要,特别是在处理大规模数据集或多线程环境时。此外,合理配置JVM参数和选择...
### Java内存泄露总结 #### 一、Java内存回收机制 Java中的内存管理主要依赖于垃圾回收(Garbage Collection, GC)机制。与C/C++等需要手动管理内存的语言不同,Java虚拟机(JVM)自动处理对象的创建与销毁过程。...
Java内存管理是Java编程中至关重要的一环,它涉及到对象的生命周期和性能优化。Java的内存管理主要由两大部分组成:对象的分配和对象的释放。对象的分配在Java中是通过`new`关键字来实现的,所有对象都在堆内存中...
Java内存管理是Java编程中的核心概念,它涉及到程序运行时如何高效地分配、使用和回收内存。在Java中,内存管理主要由JVM(Java Virtual Machine)负责,其中包括了垃圾收集机制,确保程序不会因为内存泄漏而导致...
JAVA内存溢出问题总结 JAVA 内存溢出问题是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用的内存大于虚拟机能提供的最大内存。内存溢出问题可以从容器和程序类两个方面进行排查,容器问题...
总结,JVM调优和Java内存管理是优化Java应用性能的关键,涉及垃圾回收算法、内存分区、GC类型以及各种JVM参数的调整。理解这些概念有助于我们更好地理解和优化应用程序的内存使用,从而提升整体系统性能。
### 如何解决Java内存泄漏 #### 1. 背景 Java凭借其垃圾回收机制大大简化了内存管理,使得开发者无需手动管理内存的释放,从而提升了开发效率。然而,这种自动化管理也可能成为一把双刃剑,特别是当开发人员忽视...
#### 一、Java内存管理机制 Java的一大特色在于其自动化的内存管理机制,这主要依赖于垃圾收集器(Garbage Collection, GC)来自动完成对象的内存分配与回收。尽管这一特性极大地减轻了开发者的负担,使得他们无需...
总结来看,Java内存管理是一个包含多个组件和层次的复杂系统,涉及到垃圾收集器的选择、配置以及监控等多个方面。通过本文的介绍和分析,开发者可以更深入地理解Java虚拟机中的内存管理机制,合理地选择和配置垃圾...
### Java内存泄漏解决方案详解 #### 一、Java内存泄漏概述 在Java开发过程中,经常会遇到内存泄漏的问题,尤其是在长时间运行的应用程序中更为常见。本文将详细介绍如何解决Java内存泄漏问题,帮助开发者更好地...
总结来说,Java内存泄漏的分析工具如JVisualVM、MAT、JProfiler和Arthas等,提供了丰富的功能来帮助开发者定位和解决内存泄漏问题。理解这些工具的使用方法和特性,结合实际的项目经验,可以有效地防止和修复内存...