Как я писал в предыдущей статье "Использование памяти в JAVA (Часть 1)", для более эффективного получения данных тестирования, я решил создать отдельный класс. Лучшим примером для меня показалось создать класс, содержащий в себе различные типы данных, нежели если это будет класс с однотипными элементами. В качестве членов класса я использовал типы данных, которые чаще всего применяются для написания программ. Некоторые типы преднамеренно не стал включать (например, тип short), потому что практически ими никогда не пользуюсь. Всем членам класса я установил модификатор private, их "геттерам" и "сеттерам" - public.
Последовательность строк в объекте StringArrayFactory, показанная в первой части статьи, создает строковые данные тех типов, которые созданы в классе TestClass. Для наглядности, я разместил их в обоих случаях в одинаковой последовательности. Тем самым можно будет узнать, всегда ли лучше применять объявление строго типизированного класса? Или всё таки можно обойтись чем то более простым. Конечно, многие сразу же без раздумий скажут, что класс всегда лучше массива строк хотя бы потому, что каждому отдельному члену класса будет соответствовать свой тип и для этого не понадобится выносить мозг прогамме на конвертацию из одного типа данных в другой. И я с этим так же полностью согласен. Тем не менее, что одному хорошо, не всегда другому является приемлемым. Пусть каждый сам для себя выбирает какой путь реализации для него более пригодный для использования. В данном случае, меня интересуют только цифры.
На основе этого класса создаем Factory, который вернёт нам созданный объект, учитывая тот момент, что члены класса являющиеся объектами должны создаваться динамически.
В итоге, при создании объекта, появился следующий результат:
Конечно же, вряд ли на практике может понадобиться подобный класс, но тем не менее размер впечатляет. Но не мудрено иметь такой размер: одни Calendar и Date чего стоят! Так что вполне приемлемый размер. Посмотрим, как он себя поведет при дальнейшем использовании.
java.lang.reflect.Field и java.lang.reflect.Method
Никогда бы не подумал, что мне придётся так часто использовать рефлексию в своих программах. Раньше этого как то не требовалось и даже не приходилось думать в эту сторону. В JAVA приложениях рефлексия применяется достаточно часто. И более частой задачей является получение списка членов и public-методов определенного класса, когда он ещё не объявлен. При том, этот список возвращается ввиде массива, которые получаются вызовом соответствующих функций: getDeclaredFields() для членов класса и getMethods() для методов. Последний за частую применяется для получения "геттеров" и "сеттеров" класса по известному имени члена класса. В какой нибудь из последующих статей я постараюсь осветить и показать пример этого использования.
И так как данное применение имеет место быть, я не мог оставить их без внимания. А учитывая, что класс у меня уже для этого создан, осталось только создать нужный Factory и посмотреть на результат.
Первый нам вернет массив членов класса, второй массив методов класса. Получаем соответствующие результаты:
Ну что ж, вполне компактно и эффективно. Тем не менее, выполнение этих операций обратил мое внимание на то, что как на 32-битной, так и на 64-битной системе количество байт оказалось одинаковым, когда почти все предыдущие тесты показывали хоть какую то разницу. С чем это связано я так и не пришёл однозначному выводу. В прочем это не так уж и важно.
java.util.Vector
Следующей моей задачей было определить,что первее: курицо или яйцо что лучше использовать: Vector? Или ArrayList, который якобы пришёл на смену Vector-а? На смену вроде то и пришёл, только по предыдущим тестам, которые провел Heinz и которые я проверил сам, они ничем друг от друга не отличаются. Причем методы их так же практически одинаковы. Тогда воспользуемся ими в боевой обстановке. Для этого как всегда создадим 10000 копий объекта TestClass для каждого из этих объектов. Vector будет иметь такой вид:
ArrayList своим содержанием так же не будет отличаться
Получаем соответствующие результаты тестов:
М-да... Когда я увидел результат StringArrayTCFactory, я "завязал узелок на память", что подобное создание и хранение переменных если не противопоказано, то должно хотя бы использоваться в самых крайних случаях, когда никакого другого подручного средства нет. Но я сильно сомневаюсь, что такие случаи возможны. Однако, результаты тестов с VectorTCFactory и ArrayListTCFactory дали мне повод сомневаться в этом заключении. А так ли плох массив строк? Конечно, если на карту поставлен вопрос только об использовании памяти и ни чего другого учитывать не нужно, то конечно же String[][] находится в явном преимуществе. Но, так ли он удобен и универсален в выборе? Опять таки же, ответ ещё и ещё раз говорит о том, что при проектировании той или иной программы, нужно тщательно продумывать где какой и какого вида нужен объект.
java.util.LinkedList
Ну и чтобы довести тест до конца я решил его закончить, определив количество выделяемой памяти для 10000 копий обеъкта TestClass в объекте LinkedList.
Содержание объекта ничем так же не отличается от предыдущих примеров, за исключением того, что все они будут "складироваться" в LinkedList.
Протестировав VectorTCFactory и ArrayListTCFactory, тест с LinkedList можно было и не проводить. Уже и без того было понятно, что результаты теста зашкалят и отобразят самый высокий показатель среди всех. Тем не менее, очень хотелось посмотреть на сколько этот показатель будет высокий. Как и ожидалось и как видно по результату, 10 тысяч копий обекта заняли в оперативной памяти почти 7 мегабайт(!!!).
Ну и напоследок, как я и обещал, привожу общую таблицу проведенных тестов над объектами
Конечно же, можно было бы и продолжить дальнейшее исследование с различными вариантами объектов и их сочетаниями. Однако, этот результат мне показал все, что мне требовалось. Я думаю, что приведенные результаты, вам так же помогли определиться с тем, каким методом воспользоваться для определения того или иного объекта.
Так же, для тех, кому интересно продолжить тесты, могут скачать исходые коды. Данный архив является готовым проектом для NetBeans.
И не забывайте делиться ссылками на источник, если кто то решит разместить часть данной статьи или всю ее полностью.
Спасибо за внимание.
public class TestClass {
private byte fBt;
private Byte fByte;
private char fChr;
private Calendar fCld;
private float fFlt;
private Float fFloat;
private Date fDt;
private double fDbl;
private Double fDouble;
private int fInt;
private Integer fInteger;
private long fLng;
private Long fLong;
private String fStr;
public TestClass() {
}
/**
* далее идут опеределения "гетеров" и "сетеров"
*/
}
Последовательность строк в объекте StringArrayFactory, показанная в первой части статьи, создает строковые данные тех типов, которые созданы в классе TestClass. Для наглядности, я разместил их в обоих случаях в одинаковой последовательности. Тем самым можно будет узнать, всегда ли лучше применять объявление строго типизированного класса? Или всё таки можно обойтись чем то более простым. Конечно, многие сразу же без раздумий скажут, что класс всегда лучше массива строк хотя бы потому, что каждому отдельному члену класса будет соответствовать свой тип и для этого не понадобится выносить мозг прогамме на конвертацию из одного типа данных в другой. И я с этим так же полностью согласен. Тем не менее, что одному хорошо, не всегда другому является приемлемым. Пусть каждый сам для себя выбирает какой путь реализации для него более пригодный для использования. В данном случае, меня интересуют только цифры.
На основе этого класса создаем Factory, который вернёт нам созданный объект, учитывая тот момент, что члены класса являющиеся объектами должны создаваться динамически.
public class TestClassFactory implements ObjectFactory {
public Object makeObject() {
TestClass tc = new TestClass();
tc.setfBt((byte)12);
tc.setfByte(new Byte((byte)12));
tc.setfChr('a');
tc.setfCld(Calendar.getInstance());
tc.setfDbl(123.45);
tc.setfDouble(new Double(123.45));
tc.setfDt(new Date());
tc.setfFloat(new Float(123.45));
tc.setfFlt(123.45f);
tc.setfInt(123);
tc.setfInteger(new Integer(123));
tc.setfLng(1234567891234L);
tc.setfLong(new Long(1234567891234L));
tc.setfStr(new String("Field"));
return tc;
}
}
В итоге, при создании объекта, появился следующий результат:
Factories.TestClassFactory produced Factories.TestClass which took 672 bytes
Конечно же, вряд ли на практике может понадобиться подобный класс, но тем не менее размер впечатляет. Но не мудрено иметь такой размер: одни Calendar и Date чего стоят! Так что вполне приемлемый размер. Посмотрим, как он себя поведет при дальнейшем использовании.
java.lang.reflect.Field и java.lang.reflect.Method
Никогда бы не подумал, что мне придётся так часто использовать рефлексию в своих программах. Раньше этого как то не требовалось и даже не приходилось думать в эту сторону. В JAVA приложениях рефлексия применяется достаточно часто. И более частой задачей является получение списка членов и public-методов определенного класса, когда он ещё не объявлен. При том, этот список возвращается ввиде массива, которые получаются вызовом соответствующих функций: getDeclaredFields() для членов класса и getMethods() для методов. Последний за частую применяется для получения "геттеров" и "сеттеров" класса по известному имени члена класса. В какой нибудь из последующих статей я постараюсь осветить и показать пример этого использования.
И так как данное применение имеет место быть, я не мог оставить их без внимания. А учитывая, что класс у меня уже для этого создан, осталось только создать нужный Factory и посмотреть на результат.
public class FieldFactory implements ObjectFactory {
public Object makeObject() {
try {
return Class.forName("Factories.TestClass").getDeclaredFields();
} catch (Exception e) {
System.out.println(e.toString());
}
return 1;
}
}
public class MethodsArrayFactory implements ObjectFactory {
public Object makeObject() {
try {
return Class.forName("Factories.TestClass").getMethods();
} catch (Exception e) {
System.out.println(e.toString());
}
return 1;
}
}
Первый нам вернет массив членов класса, второй массив методов класса. Получаем соответствующие результаты:
Factories.FieldsArrayFactory produced [Ljava.lang.reflect.Field; which took 1080 bytes
Factories.MethodsArrayFactory produced [Ljava.lang.reflect.Method; which took 3424 bytes
Ну что ж, вполне компактно и эффективно. Тем не менее, выполнение этих операций обратил мое внимание на то, что как на 32-битной, так и на 64-битной системе количество байт оказалось одинаковым, когда почти все предыдущие тесты показывали хоть какую то разницу. С чем это связано я так и не пришёл однозначному выводу. В прочем это не так уж и важно.
java.util.Vector
Следующей моей задачей было определить,
public class VectorTCFactory implements ObjectFactory {
public Object makeObject() {
Vector result = new Vector(10000);
TestClass tc;
for (int i = 0; i < 10000; i++) {
tc = new TestClass();
tc.setfBt((byte)12);
tc.setfByte(new Byte((byte)12));
tc.setfChr('a');
tc.setfCld(Calendar.getInstance());
tc.setfDbl(123.45);
tc.setfDouble(new Double(123.45));
tc.setfDt(new Date());
tc.setfFloat(new Float(123.45));
tc.setfFlt(123.45f);
tc.setfInt(123);
tc.setfInteger(new Integer(123));
tc.setfLng(1234567891234L);
tc.setfLong(new Long(1234567891234L));
tc.setfStr(new String("Field"));
result.add(tc);
}
return result;
}
}
ArrayList своим содержанием так же не будет отличаться
public class ArrayListTCFactory implements ObjectFactory {
public Object makeObject() {
ArrayList result = new ArrayList(10000);
TestClass tc;
for (int i = 0; i < 10000; i++) {
tc = new TestClass();
tc.setfBt((byte)12);
tc.setfByte(new Byte((byte)12));
tc.setfChr('a');
tc.setfCld(Calendar.getInstance());
tc.setfDbl(123.45);
tc.setfDouble(new Double(123.45));
tc.setfDt(new Date());
tc.setfFloat(new Float(123.45));
tc.setfFlt(123.45f);
tc.setfInt(123);
tc.setfInteger(new Integer(123));
tc.setfLng(1234567891234L);
tc.setfLong(new Long(1234567891234L));
tc.setfStr(new String("Field"));
result.add(tc);
}
return result;
}
}
Получаем соответствующие результаты тестов:
Factories.VectorTCFactory produced java.util.Vector which took 6759368 bytes
Factories.ArrayListTCFactory produced java.util.ArrayList which took 6759360 bytes
М-да... Когда я увидел результат StringArrayTCFactory, я "завязал узелок на память", что подобное создание и хранение переменных если не противопоказано, то должно хотя бы использоваться в самых крайних случаях, когда никакого другого подручного средства нет. Но я сильно сомневаюсь, что такие случаи возможны. Однако, результаты тестов с VectorTCFactory и ArrayListTCFactory дали мне повод сомневаться в этом заключении. А так ли плох массив строк? Конечно, если на карту поставлен вопрос только об использовании памяти и ни чего другого учитывать не нужно, то конечно же String[][] находится в явном преимуществе. Но, так ли он удобен и универсален в выборе? Опять таки же, ответ ещё и ещё раз говорит о том, что при проектировании той или иной программы, нужно тщательно продумывать где какой и какого вида нужен объект.
java.util.LinkedList
Ну и чтобы довести тест до конца я решил его закончить, определив количество выделяемой памяти для 10000 копий обеъкта TestClass в объекте LinkedList.
public class LinkedListTCFactory implements ObjectFactory {
public Object makeObject() {
LinkedList result = new LinkedList();
TestClass tc;
for (int i = 0; i < 10000; i++) {
tc = new TestClass();
tc.setfBt((byte)12);
tc.setfByte(new Byte((byte)12));
tc.setfChr('a');
tc.setfCld(Calendar.getInstance());
tc.setfDbl(123.45);
tc.setfDouble(new Double(123.45));
tc.setfDt(new Date());
tc.setfFloat(new Float(123.45));
tc.setfFlt(123.45f);
tc.setfInt(123);
tc.setfInteger(new Integer(123));
tc.setfLng(1234567891234L);
tc.setfLong(new Long(1234567891234L));
tc.setfStr(new String("Field"));
result.add(tc);
}
return result;
}
}
Содержание объекта ничем так же не отличается от предыдущих примеров, за исключением того, что все они будут "складироваться" в LinkedList.
Factories.LinkedListTCFactory produced java.util.LinkedList which took 6959368 bytes
Протестировав VectorTCFactory и ArrayListTCFactory, тест с LinkedList можно было и не проводить. Уже и без того было понятно, что результаты теста зашкалят и отобразят самый высокий показатель среди всех. Тем не менее, очень хотелось посмотреть на сколько этот показатель будет высокий. Как и ожидалось и как видно по результату, 10 тысяч копий обекта заняли в оперативной памяти почти 7 мегабайт(!!!).
Ну и напоследок, как я и обещал, привожу общую таблицу проведенных тестов над объектами
Test Factory | 64bit | 32bit |
---|---|---|
BasicObjectFactory | 16 | 8 |
ByteFactory | 16 | 16 |
ByteThreeFactory | 16 | 16 |
BooleanSixtyFourFactory | 80 | 72 |
StringFactory | 72 | 64 |
BooleanArrayFactory | 199608 | 200016 |
ByteArrayPrimitiveFactory | 10016 | 10016 |
VectorFactory | 199640 | 120040 |
ArrayListFactory | 199632 | 120040 |
LinkedListFactory | 399640 | 320048 |
StringArrayFactory | 520 | 408 |
StringArrayTCFactory | 5239608 | 4120016 |
HashMapSimpleFactory | 152 | 120 |
HashMapTCFactory | 65272 | 65648 |
TestClassFactory | 672 | 624 |
FieldsArrayFactory | 1080 | 1080 |
MethodsArrayFactory | 3424 | 3424 |
VectorTCFactory | 6759368 | 6280040 |
ArrayListTCFactory | 6759360 | 6280040 |
LinkedListTCFactory | 6959368 | 6480048 |
Конечно же, можно было бы и продолжить дальнейшее исследование с различными вариантами объектов и их сочетаниями. Однако, этот результат мне показал все, что мне требовалось. Я думаю, что приведенные результаты, вам так же помогли определиться с тем, каким методом воспользоваться для определения того или иного объекта.
Так же, для тех, кому интересно продолжить тесты, могут скачать исходые коды. Данный архив является готовым проектом для NetBeans.
И не забывайте делиться ссылками на источник, если кто то решит разместить часть данной статьи или всю ее полностью.
Спасибо за внимание.
Комментариев нет :
Отправить комментарий