//测试对象
public class Tree {
//外部状态
private int xCoord,yCoord,age;
public Tree(int xCoord, int yCoord, int age) {
super();
this.xCoord = xCoord;
this.yCoord = yCoord;
this.age = age;
}
//内部状态
public void display() {
//
}
}
-------------------------------------------
**
* 测试类,创建1千万个对象,查看对内存的消耗情况。
* @author 34wei12-34
*
*/
public class TreesTest {
private int length = 10000000;
private Tree[] treelt = new Tree[length];
public TreesTest() {
for(int i = 0 ;i < length ;i++) {
treelt[i] = new Tree((int)(Math.random()*length),
(int)(Math.random()*length),(int)(Math.random()*length)%5);
}
}
public void display() {
for(int i = 0 , len = treelt.length;i < len;i++) {
treelt[i].display();
}
}
}
------------------------------------------
//客户端
public class MainTest {
public static void main(String[] args) {
memoryInfo();//初始化内存的占用情况
TreesTest trees = new TreesTest();
memoryInfo();//创建对象后,内存的占用情况,创建对象消耗时间
trees.display();
memoryInfo();
}
public static void memoryInfo() {
//最大内存:maxMemory()这个方法返回的是java虚拟机(这个进程)能构从操作系统那里挖到的最大的内存
long max = Runtime.getRuntime().maxMemory();
//分配内存:totalMemory()这个方法返回的是java虚拟机现在已经从操作系统那里挖过来的内存大小,
//也就是java虚拟机这个进程当时所占用的所有 内存。
//已占用内存:在java程序运行的过程的,内存总是慢慢的从操 作系统那里挖的,基本上是用多少挖多少,
long total = Runtime.getRuntime().totalMemory();
//已分配内存中剩余的空间
//但是java虚拟机100%的情况下是会稍微多挖一点的,这些挖过来而又没有用上的内存,实际上就是 freeMemory(),
//所以freeMemory()的值一般情况下都是很小的
long free = Runtime.getRuntime().freeMemory();
long used = total - free;
System.out.println("最大内存 = " + max);
System.out.println("已分配内存 = " + total);
System.out.println("已分配内存中的剩余空间 = " + free);
System.out.println("已用内存 = " + used);
System.out.println("时间 = " + System.currentTimeMillis());
System.out.println("");
}
}
初始内存
最大内存 = 1888485376
已分配内存 = 128974848
已分配内存中的剩余空间 = 126929792
已用内存 = 2045056
时间 = 1550287881701
第一次创建对象
最大内存 = 1888485376
已分配内存 = 566231040
已分配内存中的剩余空间 = 285112856
已用内存 = 281118184
时间 = 1550287889171
第二次创建对象
最大内存 = 1888485376
已分配内存 = 566231040
已分配内存中的剩余空间 = 285112856
已用内存 = 281118184
时间 = 1550287889181
数据解析:从上面的数据可以看到对象的创建花费了大量内存,也耗费了大量的时间。有内存和耗费时间考虑使用数据分享技术,降低对象的创建个数,从而减少内存和运行时间的消耗。
下面使用享元模式:
/**
* 享元抽象角色
*/
public interface Plant {
//为这个共享函数创建抽象的享元
public void display(int x,int y,int age);
}
--------------------------------------
//具体享元类
public class Grass implements Plant {
//享元模式中需要获取外部状态。这里直接传递参数,也可以将这些对象封装成对象传递进来。
//在这里面对传递的非享元类进行操作,然后返回我们期望的结果
public void display(int x, int y, int age) {
//对传进来的外部状态进行处理。
}
}
//具体享元类
public class Tree implements Plant {
@Override
public void display(int x, int y, int age) {
//处理传递进来的外部状态
}
}
----------------------------------------
//享元工厂类
public class FlyweigthFactory {
//创建一个存放享元类的池对象
private Map<Integer,Plant> pool;
public FlyweigthFactory() {
pool = new HashMap<Integer,Plant>();
}
//根据外部对象获取我们需要的享元类
public Plant getPlant(int external) {
if(!pool.containsKey(external)) {
switch(external) {
case 0 :
pool.put(0, new Tree());
break;
case 1 :
pool.put(1, new Grass());
break;
}
}
return (Plant)pool.get(external);
}
}
----------------------------------------------------
//使用享元工厂创建多个对象,进行测试创建对象内存好时间消耗情况
public class PlantManager {
private int length = 10000000;
private int[] xArray = new int[length], yArray = new int[length],
AgeArray = new int[length], typeArray = new int[length];
private PlantFactory mPlantFactory;
public PlantManager() {
mPlantFactory=new PlantFactory();
for (int i = 0; i < length; i++) {
xArray[i] = (int) (Math.random() * length);
yArray[i] = (int) (Math.random() * length);
AgeArray[i] = (int) (Math.random() * length) % 5;
typeArray[i]= (int) (Math.random() * length) % 2;
}
}
public void displayTrees() {
for (int i = 0; i < length; i++) {
//从享元工厂中获取享元对象可以减少内存和运行时间的消耗
mPlantFactory.getPlant(typeArray[i]).display(xArray[i], yArray[i], AgeArray[i]);
}
}
}
---------------------------------------
//客户端
public class MainTest {
public static void main(String[] args) {
memoryinfo();
//创建不可共享对象
PlantManage plant = new PlantManage();
memoryinfo();
//调用享元类
plant.display();
memoryinfo();
}
public static void memoryinfo() {
//最大内存
long max = Runtime.getRuntime().maxMemory();
//当前分配内存
long total = Runtime.getRuntime().totalMemory();
//分配内存中剩余内存
long free = Runtime.getRuntime().freeMemory();
//占用多少的分配内存
long used = total - free;
System.out.println("最大内存 = "+max);
System.out.println("当前分配内存 = "+total);
System.out.println("分配内存中剩余内存 = "+free);
System.out.println("占用多少的分配内存 = "+used);
System.out.println("耗费时间 = "+System.currentTimeMillis());
System.out.println("");
}
}
最大内存 = 1888485376
已分配内存 = 128974848
已分配内存中的剩余空间 = 126930104
已用内存 = 2044744
时间 = 1550297830681
最大内存 = 1888485376
已分配内存 = 209715200
已分配内存中的剩余空间 = 47670392
已用内存 = 162044808
时间 = 1550297831983
最大内存 = 1888485376
已分配内存 = 209715200
已分配内存中的剩余空间 = 47670392
已用内存 = 162044808
时间 = 1550297832126
两次结果进行对比:
初始内存 使用享元模式
最大内存 = 1888485376 最大内存 = 1888485376
已分配内存 = 128974848 已分配内存 = 128974848
已分配内存中的剩余空间= 126929792 已分配内存中的剩余空间 = 126930104
已用内存 = 2045056 已用内存 = 2044744
时间 = 1550287881701 时间 = 1550297830681
在初始化过程中,使用享元模式消耗的时间和占用的内存都比不使用享元模式的多。时间上多1秒,内存上差别比较微小,大约3K。
创建对象
最大内存 = 1888485376 最大内存 = 1888485376
已分配内存 = 566231040 【540M】 已分配内存 = 209715200【200M】
已分配内存中的剩余空间 = 285112856 已分配内存中的剩余空间 = 47670392
已用内存 = 281118184 【268M】 已用内存 = 162044808【154M】
时间 = 1550287889171 时间 = 1550297831983
在创建对象的时候,享元模式使用的内存少和创建对象使用的时间少
对象调用display()方法
最大内存 = 1888485376 最大内存 = 1888485376
已分配内存 = 566231040 已分配内存 = 209715200
已分配内存中的剩余空间 = 285112856 已分配内存中的剩余空间 = 47670392
已用内存 = 281118184 已用内存 = 162044808
时间 = 1550287889181 时间 = 1550297832126
总结:
从上面的数据分析可以得出,使用享元模式初始化的时间较长,但在对象创建时间和内存的消耗都比较少。所以在使用享元模式时,评估使用享元模式节省的内存与多消耗的时间,是否带来了以时间换空间的效果。从上面的例子中,使用享元模式确实达到了以时间换空间的效果。