Страницы

Поиск по вопросам

Показаны сообщения с ярлыком jvm. Показать все сообщения
Показаны сообщения с ярлыком jvm. Показать все сообщения

четверг, 2 апреля 2020 г.

Сколько всего способов создать объект в java?

#jvm #java

                    
Сколько всего существует способов создать объект в java ?
Я знаю два, с new и без него. Есть ли еще способы? 

Где java использует реализацию без new, кроме сериализации?
    


Ответы

Ответ 1



Примерно такие: public static void main(String[] args) { Test test = new Test(); Class testClass = Class.forName("Test"); Test test1 = testClass.newInstance(); EnumTest enumTest = EnumTest.ONE; } class Test{} enum EnumTest{ONE;}

понедельник, 30 марта 2020 г.

Что значит аннотация @HotSpotIntrinsicCandidate?

#java #jvm #аннотации


Смотрел JVM, наткнулся на аннотацию @HotSpotIntrinsicCandidate, довольно часто ее
стал встречать. Что она значит? Раньше ее не было.
    


Ответы

Ответ 1



Начал искать дальше, вот что нашел в комментариях к аннотации(оказывается, и такие есть). The {@code @HotSpotIntrinsicCandidate} annotation is specific to the HotSpot Virtual Machine. It indicates that an annotated method may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method is intrinsified if the HotSpot VM replaces the annotated method with hand-written assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the Java libraries and is therefore not supposed to have any relevance for application code. Persons not directly involved with maintaining the Java libraries or the HotSpot VM can safely ignore the fact that a method is annotated with {@code @HotSpotIntrinsicCandidate}. Примерный перевод: Аннотация {@code @HotSpotIntrinsicCandidate} предназначена для HotSpot Virtual Machine. Это означает, что аннотированный метод может быть (но не гарантированно), встроен в HotSpot VM. Метод встроен, если HotSpot VM заменяет аннотированный рукописной сборкой и/или рукописным компилятором IR - встроенным компилятором - для повышения производительности. {@Code @HotSpotIntrinsicCandidate}аннотация является внутренней для Java библиотеки и поэтому не должна иметь никакого отношения к коду приложения. Лица, не имеющие непосредственного отношения к поддержке библиотек Java или HotSpot VM могут смело игнорировать тот факт, что метод аннотирован {@code @HotSpotIntrinsicCandidate}.

пятница, 20 марта 2020 г.

IPad и виртуальная машина Java

#ipad #java #ios #jvm


Кто-нибудь слышал об Java-машине на IPad'е? А то уж очень хочется java-код писать
под IPad, а не на Objective C.    


Ответы

Ответ 1



нет! и никогда не будет! забудьте! язык разработки приложений только обджект-си - если реально смотреть на вещи - то допустить джава машину на iOS и компания потеряет деньги. поэтому никогда джава машины не будет на iOS.

Ответ 2



The iPad Guide Does the iPad support Java? No. iPhone OS 3.2 will not support Java. The iPhone does not support Java. Steve Jobs has been quoted as saying "Java's not worth building in. Nobody uses Java anymore. It's this big heavyweight ball and chain." Java fans should not expect Apple to reverse this long-standing decision on the iPad.

четверг, 19 марта 2020 г.

Как увеличить объем выделяемой памяти программе на java?

#java #jvm


Как увеличить объем выделяемой памяти программе на java
    


Ответы

Ответ 1



Заходишь в java в панельке управления, открываешь вкладку "java", жмешь "view" и в графе "Runtime Parameters" вписываешь следущее: -XmsNm -XmxNm, где N - количество оперативной памяти, которое ты желаешь выделить для java платформы. В первом случае ты указываешь минимальный порог, а во втором максимальный. Пример: -Xms2048m -Xmx2048m ,т.е, я желаю выделить на java 2 Гб оперативной памяти.

среда, 4 марта 2020 г.

Java. Хранится ли рефлексивная информация о классе в памяти JVM?

#java #jvm #рефлексия


Вопрос в следующем, хранит ли JVM информацию о классе (например Field[], Method[],
etc.) в памяти, после загрузки класса класслоадером и можно ли держать ссылки на эту
информацию?
    


Ответы

Ответ 1



Это зависит от того, какую JVM вы используете. Например, у Hotspot JVM в свежих версиях (8+) эта информация хранится в MetaSpace, который является частью native heap. Держать ссылки на эту информацию можно, если она вам нужна. Если она не нужна, по идее лучше не держать, чтобы можно было выгрузить класс.

Ответ 2



Если я правильно понимаю вопрос, то да, jvm хранит мета-информацию о полях и методах классов в хипе. Для Java до 7 версии в PermGen, для Java 8+ в MetaSpace.

понедельник, 24 февраля 2020 г.

Когда может понадобиться самому загружать классы в java?

#java #jvm #classloader


Помогите пожалуйста разобраться с динамической загрузкой классов в java. Как я понимаю,
при старте программы загружаются классы из rt.jar, потом загружается главный класс,
а все остальные пользовательские классы загружаются по мере необходимости (например
класс A загружается только когда в программе создается объект класса A).
Первый мой вопрос заключается в следующем: что собственно значит "JVM загружает класс"?
Имеется в виду что в этот момент происходит компиляция класса?
Так же мне интересно, зачем JVM загружает класс, когда используется его статический
метод? Я провел небольшой эксперимент: написал класс, в котором есть один статический
метод и несколько нестатических. 

public class StExmp {
static{
    c = 5;
}
private int a;
public int getA() {
    return a;
}

public void setA(int a) {
    this.a = a;
}

public int getB() {
    return b;
}

public void setB(int b) {
    this.b = b;
}

private int b;
private static int c;

public StExmp(int a, int b){
    this.a = a;
    this.b = b;
    c = 5;
}

public static void show(){
    System.out.println(c);
}
}


Вызываю статический метод в главном классе StExmp.show();, потом запускаю программу
с флагом -verbose:class и вижу что когда программа дошла до вызова этого метода, она
загрузила весь класс. А почему нельзя загружать только статические члены класса? Ведь
получилось что из-за вызова одного маленького метода пришлось загрузить весь класс,
хотя он больше никак и не используется.
И главный вопрос: Когда может потребоваться самому загружать классы? Например с помощью
ClassLoader.loadClass()? Вот этого я совсем не могу понять. Ведь если мы загружаем
какой то класс, значит мы собираемся его как то использовать? Почему тогда нельзя просто
использовать его в программе (например, создать объект) JVM же сама его загрузит. А
когда это может потребоваться делать методом ClassLoader.loadClass()?
    


Ответы

Ответ 1



Ну и топик. Про загрузку классов можно много чего написать. Тема интересная, правда, уже 100 лет теорией не занимался. В контексте треда, пожалуй, отвечу на вопросы конкретные. Если в чём-то не прав, поправляйте, критика приветствуется. Первый мой вопрос заключается в следующем: что собственно значит "JVM загружает класс"? У вас есть скомпилированные байт коды классов, ClassLoader их грузить по мере необходимости. Зачем нам держать в памяти класс, если он не используется? Класс будет загружен только в момент использования. Точно так же, если на класс не осталось никаких ссылок, то ClassLoader может выгрузить класс из памяти при проходе GC. Имеется в виду что в этот момент происходит компиляция класса? Ваши классы уже скомпилированы javac. ClassLoader в память его грузит. Это если в 2-х словах, на самом деле там происходит верификация байт-кода и т.п. Так же мне интересно, зачем JVM загружает класс, когда используется его статический метод? Статически метод, константы - это метаданные класса. Не представляю, как их можно загрузить в отрыве без класс. А почему нельзя загружать только статические члены класса? Ведь получилось что из-за вызова одного маленького метода пришлось загрузить весь класс, хотя он больше никак и не используется. Предположим, есть у вас: public class StExmp { public static void show(){ System.out.println(c); } } Ок, давайте методом рассуждения выведем необходимость грузить класс. Вы хотите, чтобы метод show был загружен без класса. Но ведь в коде вы потом вызываете метод как StExmp.show()? Если класс не загружен, как вы себе представляете вызов метода? Ну хорошо, предположим загрузчик метод добавить в какую-то общую таблицу виртуальных методов. А потом вы создадите класс: public class StExmp2 { public static void show(){ System.out.println(c); } } Метод show загрузчик так же добавит его в общую таблицу статических методов? Проблему уже видите? Как потом при вызове StExmp2.show() понять какой из этих методов вызвать? Когда может потребоваться самому загружать классы? Например с помощью ClassLoader.loadClass()? Вот этого я совсем не могу понять. Ведь если мы загружаем какой то класс, значит мы собираемся его как то использовать? Почему тогда нельзя просто использовать его в программе (например, создать объект) JVM же сама его загрузит. А когда это может потребоваться делать методом ClassLoader.loadClass()? Например, у вас high-load проект. Приложение должно работать непрерывно. Но вам понадобилось заменить реализацию какого-то метода. Не перезапускать же всё приложение? Если оно стетйтлесс, то ещё ладно, но если там в памяти много данных/кэш и т.п.? Можно заменить налету. Когда это надо? Ну, скажем, вы хотите поправить какой-то критический баг, оптимизировали метод и т.п. Тут можно много чего придумать. К примеру у вас игра, в которой есть возможность добавлять кастомных npc. Вы просто пишите новые класс, который в рантайме подтягивается. Или просто неизвестно какой класс будет использоваться в итоге, решение принимаете в рантайме и грузите необходимый класс. Бывает случаи, когда классы хранятся в базе (да-да, бывает такое). Их иначе и не загрузить вовсе.

Ответ 2



Итак. По умолчанию класслоадеров всего три но вам никто не мешает определять свой. Вам никто не мешает грузить классы не из jar или из файлов, а из БД или вообще через HTTP. Каждый новый класслоадер работает в своем неймспейсе, потому они могут загружать классы с одинаковым именем, но с абсолютно разным содержимым. В качестве "простого" примера: Определим интерфейс Meower, чтоб не мучаться с рефлекшном: package pkg; public interface Meower { String meow(); } package pkg; И вот такую вот конструкцию: public class Main { static String CLASS_NAME = "Cat"; static String CLASS_V1 = "package pkg;\n" + "\n" + "public class Cat implements Meower {\n" + " public String meow() {\n" + " return \"Meo..ow\";\n" + " }\n" + "}\n"; static String CLASS_V2 = "package pkg;\n" + "\n" + "public class Cat implements Meower {\n" + " public String meow() {\n" + " return \"Mrr, meo..ow\";\n" + " }\n" + "}\n"; public static void main(String[] args) throws Exception { // Делаем из строки SourceFile SourceFile srcv1 = new SourceFile(CLASS_NAME, CLASS_V1); SourceFile srcv2 = new SourceFile(CLASS_NAME, CLASS_V2); // Компилируем и загружаем ClassLoader clrv1 = new MemoryClassLoader(srcv1); ClassLoader clrv2 = new MemoryClassLoader(srcv2); // Берем наш класс Class clazzv1 = (Class) Class.forName("pkg." + CLASS_NAME, true, clrv1); Class clazzv2 = (Class) Class.forName("pkg." + CLASS_NAME, true, clrv2); // Инстанцируем экземпляры Meower ov1 = clazzv1.newInstance(); Meower ov2 = clazzv2.newInstance(); // Мяукаем System.out.println(ov1.meow()); // Meo..ow System.out.println(ov2.meow()); // Mrr, meo..ow } } Иными словами, немного помучавшись и дописав какую-нибудь Factory я смогу(хоть и не рекомендую этого делать :)) в рантайме, без остановки JVM, брать исходники откуда-нибудь, компилировать их на ходу и менять поведение остальной программы. Также, учитывая сказанное выше, можно придумать еще несколько причин вызвать Class.forName, наиболее распространенный - заранее прогрузить, а не затормозить когда вызовут. Менее распространенный - если вы грузите не с локальной файловой системы и хотите убедиться заранее что класс доступен. ПС: исходники MemoryClassLoader и SourceFile я сюда не выкладываю чтоб не получилась портянка, но они легко гуглятся.

Доступ к приватным полям класса предка

#jvm #java


Насколько я понимаю, при создании объекта класса через new выделяется область в Heap
для хранения всех полей как самого класса, так и нестатичных публичных (и protected)
полей всех его предков. А затем поочерёдно вызываются конструкторы всех предков, начиная
со старшего, которые инициализируют, а возможно перезаписывают значения полей данного
объекта. И в конце конструктор самого объекта-наследника инициализирует свои новые
поля или, возможно, перезаписывает значения полей, которые уже инициализировали конструкторы
предков.
Вопрос в следующем. Если у предка есть приватные поля и публичные геттеры к ним,
а наследник не переопределяет эти поля и геттеры, то из объекта наследника можно, вызвав
унаследованный геттер, получить значение поля из приватного поля предка. Как происходит
в действительности? При создании объекта создаются объекты всех его предков в отдельных
областях памяти, со всеми своими полями? Или же, что мне кажется более вероятным, JVM
понимает, что при наличии публичных геттеров у предка наследник может получить доступ
к его приватным полям и поэтому создаёт в области памяти объекта-наследника приватные
поля его предка?
    


Ответы

Ответ 1



Объектный модуль класса, помимо прочей информации, содержит информацию о иерархии наследования, порядке следования полей и их размерах. То есть после загрузки класса у виртуальной машины в метаспейсе всегда есть "карта" объектов этого класса, по которой можно вычислить по какому смещению от начала блока памяти находится то или иное поле. При создании нового объекта выделяется блок достаточного объёма, чтобы хранить заголовок объекта, его поля, а также поля всех его суперклассов. Причём поля располагаются в порядке от корня наследования - сначала поля суперкласса, потом подкласса. Благодаря такому расположению "карта" суперкласса подходит для ориентирования в объекте подкласса. Для наглядности определим примитивную иерархию классов class A { int x; int y; } class B extends A { int z; } Операция B obj = new B() выделит в куче такой блок ----------- -- | Заголовок | | ----------- |_ Класс A | x | | | y | | ----------- -- | z | |- Класс B ----------- -- Если мы теперь приведём тип объекта к базовому, то виртуальная машина будет выполнять операции доступа к полям объекта так, будто никакого хвостика, содержащего поле z, просто не существует. Благодаря Алексею Шипилёву мы можем увидеть это вживую с помощью инструмента jol (Java Object Layout). import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.vm.VM; public class ShowLayout { public static void main(String[] args) throws Exception { System.out.println(VM.current().details()); System.out.println(ClassLayout.parseClass(B.class).toPrintable()); } } Компилируем javac -cp jol-cli-0.9-full.jar ShowLayout.java Запускаем java -javaagent:jol-cli-0.9-full.jar ShowLayout Получаем # Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift. # Objects are 8 bytes aligned. # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int A.x N/A 16 4 int A.y N/A 20 4 int B.z N/A Instance size: 24 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total Для методов каждого класса у JVM тоже есть "карта", указывающая по какому смещению в памяти находится начало того или иного метода. Есть два способа использования этой "карты" - раннее и позднее связывание (на самом деле больше, но это несущественно в текущем контексте). Обычный вызов метода obj.getZ() будет скомпилирован в байткод aload_1 // Загрузка в стек ссылки на obj invokevirtual #4 // Method B.getZ:()I Инструкция invokevirtual использует позднее связывание. То есть при вызове метода JVM анализирует контекст вызова (call site), определяет какой именно метод нужен и передаёт управление по требуемому смещению. Благодаря этому и возможен полиморфизм. Вызов метода суперкласса (а также вызовы конструкторов и приватных методов) super.getX(); будет скомпилирован в байткод aload_0 // Ссылка this на объект класса B invokespecial #2 // Method A.getX:()I Инструкция invokespecial использует ранее связывание. То есть ещё на этапе загрузки класса понятно какой именно метод какого именно класса надо будет вызвать, и JVM "зашивает" смещение этого метода в байткод. Получив управлением метод getX (с помощью JVM, конечно) отсчитает нужное смещение от начала блока памяти, на который указывает переданная ссылка, до места где должно располагаться поле x в классе A, прочитает его значение, положит на вершину стека и вернёт управление вызывающему коду. Даже не догадываясь, что ссылка this на самом деле указывает на объект большего размера, и есть ли в подклассе метод с таким же именем, как у него.

среда, 5 февраля 2020 г.

Первая JVM для платформы Java SE

#java #jvm #интерпретатор #hotspot


Собственно говоря, сам вопрос кроется в залоговке данной темы. Знаю, что примерно
с 2002 года освной реализацией JVM для платформы Java SE является всем известный канонический
«HotSpot», изначально разработанный компанией «Longview Technologies», которая затем
была поглащена Sun Microsystems. На тот момент времени, вышеуказанная JVM создавалась
для версии 1.3 платформы Java SE. 

А что было раньше? Какая именно JVM использовалась в самых ранних версиях? Официальной
датой релиза самого языка принято считать 23-е мая 1995-го года. Какая же JVM была
наиболее популярна в 1995-1996 годах и вплоть до появления «HotSpot»? Пытался найти
данную информацию в глобальной сети, но ничего не получилось. Также, если позволите,
хотелось бы узнать, на каком языке программирования написано большинство JVM и есть
ли какая-нибудь JVM, которая была написана на чистой Java'е (также интересует ЯП на
котором была написана первая JVM). Благодарю за ответ!
    


Ответы

Ответ 1



Сохранившиеся оригинальные сановские версии JDK/JRE/JVM можно скачать здесь - это версия 1.1, более ранние версии увы не сохранились... HotSpot пошел с версии JDK 1.2, до этого они просто назывались Sun JVM, потом когда Sun начал направо-налево лицензировать разные инкарнации JVM появилась необходимость отделить ее от остальных JVM. Наиболее известная альтернативная инкарнация JVM была JRockit, которую вовсю понужал Bea Systems на своем сервере WebLogic - она вышла по-моему 1998 году - как то так и была настолько хороша, что было модно говорить, что Sun JVM скоро умрет :) Я еще помню версии JVM 0.8/0.9, но уже в 2004 году я их не мог найти Почти все JVM пишутся на смеси C/Java

Ответ 2



JVM HotSpot впервые стала использоваться в Java 1.2 в 1999-м. Судя по всему, у предыдущей виртуальной машины Sun просто не было названия. JVM по имени JVM. Подозреваю, что имя потребовалось тогда, когда Microsoft сделал свою виртуальную машину, с нарушениями JLS и JIT-компилятором. Большинство JVM написаны на C. На Java написана GraalVM.

среда, 29 января 2020 г.

Можно ли средствами Java убить дочерние процессы процесса, созданного с помощью ProcessBuilder?

#java #jvm #процесс #multiprocessing


Если создать процесс 

ProcessBuilder builder = new ProcessBuilder("command");
final Process process = builder.start();


то его можно будет убить с помощью: process.destroy();

Но что, если этот command наплодит кучу других процессов? process.destroy(); уже
будет не в силах убить их. И они останутся в памяти.

При этом, если из командной строки сделать Ctrl+C, то дочерние процессы тоже завершатся.

В сети много вопросов на эту тему (большинство из них старые) и почти все ответы
говорят о том, что это невозможно сделать средствами JVM - нужно обращаться к ОС. Решил
задать вопрос, в надежде на то, что это всё же стало возможным с появлением Java 8.
Хочется получить решение, не зависимое от ОС.

Возможно ли на сегодняшний день средствами Java убить дочерние процессы процесса,
созданного с помощью ProcessBuilder, не манипулируя напрямую с командами ОС? Если возможно,
то как это реализовать? Если нет, то ожидается ли такая возможность в Java 9?

UPDATE

Хочу ещё раз выделить то, что я не ищу решения, зависящие от ОС. По этой ссылке можно
прочитать о том, как искать PID процесса в Unix и Windows. Потом можно будет обратиться
к терминалу с соответствующей командой для "убийства" процесса. 

Это не является темой данного вопроса.
    


Ответы

Ответ 1



В версиях Java до 8й включительно инструментарий для работы с процессами был довольно скудным. Но если ознакомиться, с JEP 102: Process API Updates (который реализуется в рамках Java 9), то мы увидим, что Brian Goetz говорит нам о следующих вещах: Возможность получить pid процесса JVM и pid-ы процессов, запущенных средствами API. Возможность получить список запущенных в системе процессов, включая их pid, состояние, наименование и, возможно, потребление ресурсов. Возможность взаимодействовать с деревьями процессов, а именно прекращать работу целого дерева. Возможность взаимодействовать с сотнями дочерних процессов, возможно, мультиплексируя потоки вывода и ошибок, чтобы избежать создания отдельной нити (thread) на каждый процесс. Все эти радости уже можно потрогать в 9ке: см. интерфейс ProcessHandle: long getPid() static Stream allProcesses() и ProcessHandle.Info info() Stream descendants()

Ответ 2



Если речь идет о "популярных" ОС для Java (Windows & unix-like), то на первой можно запустить программу taskkill/PID , а на вторых - kill-9 . На юниксе, кроме того, можно отправить сигнал группе процессов (process group id равен - процесса-родоначальника группы. Или воспользоваться библиотекой, дергающей нативные методы, типа Posix for Java (впрочем, для метода kill достаточно простого JNI вызова. Но средствами именно Java, да без сторонних библиотек, насколько я знаю, нет. Правда, говорят, что у Process так просто pid не получишь, но вот можно через Reflection достать приватное поле UnixProcess'а pid.. :) В 9-ой версии OpenJDK Process имеет метод getPid(), так что, теперь вышла нам всем поблажка в плане получения pid. Однако, никаких переносимых методов для управления группой процессов пока не просматривается.

Ответ 3



Кроссплатформенного решения нету. Придётся писать для каждой ОС самому. Не так всё просто, по-этому вряд ли кто-то даст законченное решение. Мы можем только дать какие-то подсказки, пути к решению задачи. Unix Сначала надо получить PID нашего процесса. /** * Получить строку - pid программы - Java VM */ public static String getPid() throws IOException,InterruptedException { Vector commands=new Vector(); commands.add("/bin/bash"); commands.add("-c"); commands.add("echo $PPID"); ProcessBuilder pb=new ProcessBuilder(commands); Process pr=pb.start(); pr.waitFor(); if (pr.exitValue()==0) { BufferedReader outReader=new BufferedReader(new InputStreamReader(pr.getInputStream())); return outReader.readLine().trim(); } else { System.out.println("Error while getting PID"); return ""; } } С другой стороны java.lang.Process - абстрактный класс, конкретная реализация зависит от ОС. На Linux это java.lang.UnixProcess, у которой есть приватное поле pid. Используя рефлексию можно запросто получить поле: public static long getPidOfProcess(Process p) { long pid = -1; try { if (p.getClass().getName().equals("java.lang.UNIXProcess")) { Field f = p.getClass().getDeclaredField("pid"); f.setAccessible(true); pid = f.getLong(p); f.setAccessible(false); } } catch (Exception e) { pid = -1; } return pid; } Дальше необходимо получить список подпроцессов. Есть комманда pstree ${pid}. Можно выполнить команду из Java Process p = Runtime.getRuntime().exec("pstree ${pid}"); Затем спарсить список и вытащить PID'ы всех процессов. Потом используя Runtime.getRuntime().exec() убить все процессы по PID'ам, если надо. Windows Для получения PID'а можно сделать что-то такое. Тут сложнее. Я знаю, что можно получить список запущенных процессов. Process p = Runtime.getRuntime().exec("cmd /c tasklist"); StringWriter writer = new StringWriter(); IOUtils.copy(p.getInputStream(), writer); String theString = writer.toString(); Но вопрос как найти зависимости между процессами не ясен. Как минимум, можно выполнить tasklist и отфильтровать список по имени процесса. Процесс с большим PID вероятно и есть ваш процесс. Так вы получите PID вашего главного процесса. Можно попробовать запустить PowerShell скрипт для получения списка процессов (а потом отфильтровать по parent id). Такая команда в шеле: gwmi win32_process |select ProcessID,ParentProcessID,Name, @{l="Username";e={$_.getowner().user}}|where {$_.Username -ne "SYSTEM"} | where {$_.Username -ne "LOCAL SERVICE"} | where {$_.Username -ne "NETWORK SERVICE"} | where {$_.Username -ne $null} |Sort-Object ProcessID | ft -AutoSize Даст что-то такое: ProcessID ParentProcessID Name Username --------- --------------- ---- -------- 180 4536 chrome.exe Suvitruf 396 5272 slack.exe Suvitruf 1488 1040 taskeng.exe Suvitruf 1504 4008 BatteryLife.exe Suvitruf 1540 180 chrome.exe Suvitruf 1704 180 chrome.exe Suvitruf 2084 180 chrome.exe Suvitruf 2404 5272 slack.exe Suvitruf 2408 5272 slack.exe Suvitruf Вам надо понять как эту команду выполнить с использованием Runtime.getRuntime().exec(). После этого распарсить ответ и получить список PID'ов процессов. Для их убийства вызывать: String cmd = "taskkill /F /PID " + tokill; Runtime.getRuntime().exec(cmd);

вторник, 28 января 2020 г.

Почему java программа не освобождает память?

#java #память #jvm #сборщик_мусора


У меня тут творятся очень странные вещи с памятью. Есть главный класс в котором main
метод запускает множество потоков. Эти созданные потоки через какое то время убиваю,никаких
объектов и потоков но память выросла и не освобождается..наблюдаю за всем этим в профайлере(JProfiler).
Нет никаких потоков и обьектов кроме main, но память все так же стоит и не освобождается,
когда запускаю новые потоки память растет с того же места на котором остановилось до
этого. т.е. похож на утечку памяти. но вроде никаких утечек нет. В памяти висят не
очень много обьектов,самый большой это массив char[] Посмотрите на картинках: в диспетчере
задач показывает что программа съедает 259 мб.

и на данный момент нет ничего кроме метода main который в состоянии Thread.sleep().



ниже объекты которые в памяти :



И вот общие сведения о том сколько жрет моя программа : 


Объясните, что тут к чему? Почему если в памяти нет почти никаких объектов  приложение
занимает 81 мб(это то что написано в профайлере). Как будто приложение распухло но
внутри ничего нет.
    


Ответы

Ответ 1



У Java есть свой heap, в котором аллоцируются объекты. Когда объект освобождается сборщиком мусора, освобождается место именно в хипе Java, а не в общесистемном. Сборщики мусора могут иметь разные стратегии того, как они отдают память обратно системе. Кроме того, JVM использует и системный heap для своих нужд, например для стеков ваших потоков (которых вы создаете множество).

Ответ 2



Это обычное поведение JVM. JVM управляет памятью через некоторый промежуточный артефакт, т.н. heap - большой (огромный) кусок памяти, в котором по мере необходимости создаются (и удаляются) объекты, но сам heap как был аллоцирован, так и остается. В целом JVM может отдать часть heap обратно ОС, но прямых способов воздействия на этот процесс нет, плюс из-за характера использования с этим могут быть некоторые проблемы (например, сначала придется компактить содержимое хипа).

Java.Польза от ссылок(SoftReference, WeakReference , PhantomReference)

#java #jvm #сборщик_мусора


Используя различные ссылки можно получить большую скорость работы сборщика мусора
или для иных целей используются ссылки?    


Ответы

Ответ 1



Разница не в скорости, разница в том, как сборщик мусора будет работать с объектом по ссылке. SoftReference — это самая сильная из всех перечисленных ссылок. Если на объект не осталось больше нормальных ссылок, а только SoftReference, объект не будет съеден сборщиком мусора до тех пор, пока реально не возникнет ситуация нехватки памяти. Хороший пример использования для таких ссылок -- кеш больших картинок в памяти. Если память исчерпалась, картинку выбросит сборщик мусора, и вы сможете перечитать её с диска, когда она снова вам понадобится. WeakReference слабее: она не увеличивает дополнительно время жизни объекта, на который ссылается, и если на объект есть не более чем слабые ссылки, сборщик мусора может убрать его когда ему вздумается. Хороший пример использования для таких ссылок -- добавить дополнительную информацию об каком-то объекте. Для этого вы держите в своём контейнере не сам объект, а лишь WeakReference на него, вместе с необходимой информацией, тем самым вы не мешаете объекту умереть вовремя и не меняете свойства остальной части программы. Имея на руках SoftReference или WeakReference на ещё живой объект, вы можете получить настоящую ("сильную") ссылку, и предотвратить съедение этого объекта сборщиком мусора. Имея сильную ссылку, вы можете работать с объектом как обычно. PhantomReference ещё слабее. Она не только не предохраняет объект от уборки, она даже не даёт возможности получить сильную ссылку. Вы можете только узнать, что объект собирается умереть, и предпринять какие-то действия по очистке; предотвратить смерть объекта вы не сможете. При создании SoftReference, WeakReference вы можете, а при создании PhantomReference должны указать ReferenceQueue (хотя тут можно указать null, это обычно бессмысленно). После того, как объект будет убран сборщиком мусора, ссылка попадает в указанную вами ReferenceQueue. Для нефантомных ссылок при добавлении в очередь финализатор уже выполнен и память объекта уже освобождена, но для фантомных добавление происходит после вызова финализатора до очистки памяти. Вы можете по сути не объявлять дорогой финализатор, а воспользоваться фантомной ссылкой из очереди для того, чтобы самостоятельно освободить ассоциированные ресурсы. (Для этого вы не сможете использовать сам объект, т. к. сильную ссылку на него невозможно получить из фантомной ссылки; но вы можете унаследоваться от PhantomReference, чтобы добавить нужную информацию в объект-ссылку.) Ещё одно отличие, как подсказывает @gstackoverflow в комментариях, состоит в том, что фантомную ссылку вы должны очистить вручную, иначе объект будет оставаться (фантомно) достижим. Только после этого объект будет окончательно удалён. Источники: https://stackoverflow.com/q/3329691/276994 https://habrahabr.ru/post/169883/ http://www.javaportal.ru/java/articles/referenceclasses.html

Ответ 2



Скоростью управлять не получиться. Но более эффективно использовать память - да. PhantomReference Вам навряд ли придется использовать. Почитайте о них.

понедельник, 6 января 2020 г.

ignoring option MaxPermSize=512m при запуске Scala

#java #jvm #scala


Установил Play фрэймворк, и при запуске проекта выдается ошибка:


  Java HotSpot(TM) 64-Bit Server VM warning:
  ignoring option MaxPermSize=512m; support was removed in 8.0


Как исправить?


  [error]Server access Error: Connection timed out: connect url=http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/com.typesafe.sbt/sbt-native-packager/scala_2.10/sbt_0.13/0.6.4/ivys/ivy.xml


Я так понял, он пытается что-то скачать, но доступа нет из-за этой ошибки?

ОС: Windows 8
Версия Play: play-2.2.6
Версия Java: 1.8.

Нельзя ли по-другому это обойти?
    


Ответы

Ответ 1



Первое - это не ошибка, а предупреждение. Второе. Установить прокси в браузере недостаточно, его нужно установить на уровне операционной системы. Вы не указали какую используете ОС и версию Play, поэтому могу предложить воспользоваться общим решением через activator. Запустите его так: activator -Dhttp.proxyHost="your proxyname" -Dhttp.proxyPort="your port" -Dhttps.proxyHost="your proxyname" -Dhttps.proxyPort="your port" Дополнительно может потребоваться установить параметры proxyUser и proxyPassword Обновление Для Windows 8 и Play 2.2.6 можно решить проблему воспользовавшись этой инструкцией в документации, а именно: Еслы вы находитесь за прокси, убедитеcь что установлено для Windows set HTTP_PROXY=http://<логин>:<пароль>@<хост>:<порт> От себя отмечу, что логин и пароль может и не нужен, если прокси его не требует.

Ответ 2



Вы можете попробовать установить offline := true в файле build.sbt. Во многих случаях так работает.

Ответ 3



Все просто, хоть я и сам потратил какое-то время на решение этой задачи: 1-перейти в каталог cd opt/glassfish4/bin/ 2-запустить с ROOT правами sudo ./asadmin start-domain 3-остановка сервера тоже с ROOT правами sudo ./asadmin stop-domain В вашем случае все должно заработать.

Исполняемые файлы Java

#jvm #java


Вопрос такой, если создать .jar или .exe исполняемый файл, он запустится на компьютере
на котором нет JVM и не установлены JDK и JRE?
    


Ответы

Ответ 1



Т.к. jar в принципе не может работать без JVM (потому что jar - это набор классов в виде байт-кода, а не в виде исполняемых бинарных файлов), то сформулируем Ваш вопрос так: как в один .exe-файл упаковать свой .jar-проект и JVM. Чтобы Ваш проект мог запускаться и работать без предустановки JVM? На stackoverflow описали успешный эксперимент по скрещиванию launch4j и JVM (т.е. JRE). Делюсь ссылкой: как упаковать свой JAR-проект и JRE в один EXE-файл через launch4j. Перевожу ответ из ссылки: 1.Упаковать свое приложение и JRE в один ZIP-архив со структурой директорий: containerFolder |- jre |-bin (здесь лежит java.exe из состава JRE) |-lib |- cfg (папка для сохранения user-конфигурации, если нужно) |- bin (Ваше приложение с .exe и вашим jar-файлом и другими Вашими внешними файлами из проекта) 2.В xml-файле для launch4j сконфигурировать JRE таким образом: ../jre -DgvSIG.confDir=../cfg Фишка состоит в том, что путь указывается не к файлу java.exe. Путь к java.exe указывается относительно позиции .exe-файла. В данном примере, в качестве JRE, используется обычная копия стандартного установленного JRE-движка (например: C:\Program Files\Java\jre1.8.0_121). И да, Launch4j - бесплатный и с очень демократичной лицензией BSD 3-Clause License.

Ответ 2



Нет, не получится. Вы хорошо подумали? Есть такие упаковщики, которые в полученный EXE пакуют версию JRE/JVM, в которой и будет запускаться jar. Например ExcelsiorJET - правда, он коммерческий, но если поискать я думаю есть и бесплатные.

Ответ 3



Нет, не получится. Есть только одна альтернатива. Некоторые упаковщики в exe, могут привязать его к локальному jvm. То есть вместе с exe придется таскать папку с jre.

Откуда onClick берет значение константы, ведь метод, в локальном контексте которого она существовала завершился и ее больше быть не должно?

#java #android #jvm


Я сидел, никого не трогал, изучал андроид SDK, писал простенькие приложения, как
вдруг задумался над одним куском кода и понял, что я в принципе не понимаю как и почему
он работает:

public void onBindViewHolder(ViewHolder holder, final int position) {
    CardView cardView = holder.cardView;
    ImageView imageView = (ImageView) cardView.findViewById(R.id.image_info);
    Drawable drawable = ContextCompat.getDrawable(cardView.getContext(), imgResIds[position]);
    imageView.setImageDrawable(drawable);
    ((TextView) cardView.findViewById(R.id.text_info)).setText(captions[position]);
    if(listener != null){
        cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.onClickListener(position);
            }
        });
    }
}


То, что никак не укладывается в моей голове - это константа position, в моем понимании
эта констант существует как локальная переменная метода

onBindViewHolder


следовательно на момент создания объекта типа View.OnClickListener она определена
и существует, но вызов onBindViewHolder завершится раньше, чем будет вызван метод onClick
ранее созданного объекта типа View.OnClickListener и я не понимаю откуда onClick берет
значение этой константы, ведь метод, в локальном контексте которого она существовала
завершился и ее больше быть не должно.

Неужели при такой перегрузке метода он запомнит ссылку на эту константу и она продолжит
существовать? Объясните пожалуйста как это работает!
    


Ответы

Ответ 1



Локальные переменные создаются на стеке и уничтожаются при выходе из метода. final переменные изначально тоже на стеке. Но для анонимного класса View.OnClickListener эта final переменная будет скопирована в хип и будет создано синтетическое поле внутри анонимного класса. Так что, даже когда метод завершится, очистится только локальный стек, копия final переменной останется внутри анонимного класса.

суббота, 4 января 2020 г.

Java Memory Model и happens-before

#java #многопоточность #jvm


Добрый день. Есть небольшой вопрос по JMM. Я знаю как работает  happens-before, но
не могу понять один момент. Вот код:

private static int x = 0;
private static volatile boolean flag = false;

public static void main(String[] args) throws InterruptedException {

  new Thread(() -> {
      x = 10;
      while (!flag) ;
      System.out.println(x);
  }).start();

  x = 5;
  flag = true;
}


Какое значение должно принять X? Если какое-то правило чтобы определить это?
    


Ответы

Ответ 1



Если изобразить потоки выполнения в вашем коде, мы получим такую картину: Main thread - это основной поток вашей программы, Thread1 - поток, который вы явно создаете. Запись и чтение волатильной переменной flag действительно создают отношение Happens-Before (E → B на диаграмме). Таким образом по отношению к вызову System.out.println(x); у нас есть два потока выполнения, для каждого из которых гарантируется порядок выполнения: A → B → C и D → E → B → C. Запись в переменную x произойдет гарантированно раньше ее чтения. Но вот инструкции записи в переменную x (A и D) находятся в состоянии гонки (race condition) и их порядок выполнения друг относительно друга не определен. В итоге то, что мы видим на диаграмме никак нельзя назвать полным порядком. JVM вполне может выполнить инструкции следующим образом:

Ответ 2



Мы не знаем какое значение увидит инструкция System.out.println(x); по нескольким причинам: Поток может прочитать значение, установленное в нем (независимо было ли оно установлено раньше или позже другого потока) или значение, установленное в другом потоке. В виду того что гарантии на то, какое значение прочитает поток не даются. В виду того, что невозможно определить время выполнения инструкции в различных поток относительно других потоков - неизвестно в какой момент потоки выполнят инструкции x = 10 и flag = true относительно друг друга. Относительно happens-before происходящего с volatile переменной. В данном случае если чтение volatile переменной происходит после записи ее переменной, то операция чтения ОБЯЗАТЕЛЬНО будет видеть "новые" данные. В данном случае нам это говорит лишь о том, что поток который находится в цикле выйдет из него, как только будет выполнена инструкция flag = true. Но опять же о том, когда будет выполнена инструкция flag = true нам неизвестно, она может быть выполнена как до, так и после чтения (одного или нескольких) значения из volatile переменной в порожденном потоке. Необходимо отметить, что отношение, изображенное на картинке в ответе Nofate не должно трактоваться, как обязательный порядок индукцией или блокировка читателя до момента пока не будет установлено значение volatile переменной. “Запись в переменную x произойдет гарантированно раньше ее чтения.” Поток просто будет находится в бесконечном цикле до момента выполнения flag = true.

вторник, 31 декабря 2019 г.

Полная компиляция java-программы при её запуске

#java #jvm


Возможно ли заставить JVM (от Oracle) компилировать приложение не JIT (только ту
часть, которая была задействована), а полностью?
Возможно виртуальные машины других поставщиков могут это делать?
Возможно ли как-то влиять на оптимизацию кода (типа как в GCC -02, -03 и т.д.)?
P.S. Идея состоит в том, чтобы на взгляд пользователя приложение долго запускалось,
но, главное, никаких тормозов в ходе исполнения не ощущалось.    


Ответы

Ответ 1



Лично мое мнение что это пустая трата времени, но если вам так хочется, то есть специальные продукты для Ahead Of Time компиляции Java приложений: Excelsior JET RoboVM Первый примечателен тем что разрабатывается уже давно (кстати русской командой раработчиков). Второй все еще в стадии alpha, но планируется обширная поддержка различных платформ (Windows пока не всписке, зато есть Android и iOS). Думаю если хорошо погуглить, то можно будет найти еще инструменты для AOT компиляции.

Ответ 2



Возможно ли заставить JVM (от Oracle) компилировать приложение не JIT (только ту часть, >> которая была задействована), а полностью? Да можно. воспользовашись AOT копилятором, но тут уже о VM речи не идет. P.S. Идея состоит в том, чтобы на взгляд пользователя приложение долго запускалось, но, >> главное, никаких тормозов в ходе исполнения не ощущалось. Чтобы победить тормоза нужно понять откуда они идут. Самая частая проблема - нехватка оперативной памяти, что она под собой несет: когда у Вас появляется куча объектов которые пытается собрать GC, когда работает GC то происходит стоп ворлд, когда замирает все приложение для VM это просто sw для пользователя тормоз. можно попробовать прикрутить GC который это не делает, например G1 или затюнить уже имеющийся. Попробовать использовать другую JVM, например JRockit они позиционируют себя как реалтайм машина. Я это все к чему, мне кажется, что если приложение тормозит, то тут вина не в том. java транслирует байт код, в конце концов трансляция байт-кода, по сравнению с той-же транзакцией к базе не самая ресурсоемкая операция. Ищите причину тормозов, а после думайте как это оптимизировать.

Ответ 3



Можно попробовать запустить с опцией -server и/или настроить gc (например, включить параллельный сборщик мусора -XX:-UseParallelGC). По опыту могу сказать что тормоза обычно от сборщика мусора.

Ответ 4



Не думайте, что чем раньше JIT-компилировать, тем лучше. HotSpot Server JVM в режиме интерпретатора не просто выполняет код, а ещё и статистику собирает, как он работает, какие ветки выполняются чаще, какие реальные методы вызываются на виртуальных вызовах и т. д. Потом код компилируется в машинный код, но не очень быстрый, который продолжает собирать статистику. А уже потом эта информация используется для более эффективной JIT-компиляции. В этом преимущество JIT-компиляции перед AOT-компиляцией: вы можете произвести разный машинный код в зависимости от того, как приложение реально работает. Это может его сделать быстрее. В некоторых экстремальных случаях сбор статистики позволяет скомпилировать код так, что он будет в 10 раз быстрее, чем слепая JIT-компиляция.

суббота, 28 декабря 2019 г.

Особенности полиморфизма

#java #jvm #полиморфизм


Всем доброго времени суток.

Только начал готовиться к собеседованию на Java Junior`а, как произошло небольшое
недопонимание по поводу полиморфизма. Я изучал Java Core по книге Кэти Сьерра и Берта
Бейтса("Изучаем Java. 2-е издание"). 

Там механизм наследования (и вообще хранения объектов в куче JVM) представлялся вот так:

public class A{
//code...
}

public class B extends A{
//code...
}

public class TestApp{

public void init(){
B val = new B();
}

}


И в JVM это все выглядит таким образом:



Допустим, у нас есть такой код:

public class Animal{

public void makeSomthing(){
System.out.println("Animal"); 
} 

}


public class Dog extends Animal{

//переопределенный метод
@Override
public void makeSomthing(){
System.out.println("Dog");
} 

public void fMagic(){

this.makeSomthing(); //Console: Dog    
super.makeSomthing(); //Console: Animal [?]   

}

}


public class TestApp{

public static void main(String[] args){

Dog dogPet = new Dog();

dogPet.fMagic();

}

}


Выходит, что при вызове метода fMagic() у объекта dogPet так же вызывается метод
makeSomthing() у "внешнего" объекта Dog и еще этот же метод вызывается у "внутреннего"
объекта Animal(у суперкласса). Как я понял, по логике этой книги, конструкция переопределения(@Override)
методов заключается в том, что метод по сути один, а когда мы его переопределяем, то
как-бы изменяем его во "внутреннем" объекте c помощью "внешнего". А уже при вызове
dogPet.fMagic() он вызывается изнутри.



Но при этом:

класс Dog

   public void fMagic(){

    this.makeSomthing(); //Console: Dog
    super.makeSomthing(); //Console: Animal [?]

    //Console:  Dog@74a14482 -  адрес Dog. Dog@74a14482 - адрес Animal. 
    System.out.println("links:"+"\n" + this + " -  адрес Dog. "+ super.getObjectLink()
+ " - адрес Animal.");

}


класс Animal

 public Object getObjectLink(){ return this; }


Но самое странное на мой взгляд заключается вот в этом:
При сужении типа до его родителя, вызов функции дает "Dog", а не "Animal". Т.е в
данном случае, переопределение метода в классе Dog изменяет метод saySomthing(). А
вот в случае вызова метода fMagic() у класса Dog, вызываются 2 разные функции И переопределение
функции суперкласса, как я понимаю, ничего не дает. 

 Animal dogPet = new Dog();

 dogPet.makeSomthing(); //Console: Dog


Суть вопроса: Как же именно все эти процессы и механизмы ООП происходят на самом
деле? И почему же при вызове fMagic() на консоль выводится две разные надписи "Dog"
и "Animal", а не одна и та же надпись "Dog", как в случае с Animal dogPet = new Dog();?
    


Ответы

Ответ 1



Во-первых завязывайте с такой терминологией, нет никаких внешних и внутренних объектов. У вас есть класс Dog, который расширяет класс Animal. Наследование представляет собой отношение типа является(is a). Следовательно в Вашем случае объект Dog является объектом Animal, а значит содержит в себе его поведение. Поведение родительского класса может использоваться в подклассе при помощи ключевого слова super, что Вы и продемонстрировали. Ключевое слово this используется для получения объектом ссылки на самого себя, что в данном случае делается по-умолчанию. Т.е. Вы получите такой же результат если опустите его. Чтобы лучше понять преимущества полиморфизма разберите паттерн проектирования "Стратегия". На мой взгляд один из самых ярких примеров. Также если Вы пишете Animal dog = new Dog(); Вы говорите машине: "Создай мне объект Dog и помести его в переменную типа Animal". Вы можете это сделать, поскольку Dog является Animal. Однако обращаясь к этой переменной у Вас будет вызываться метод класса Dog поскольку в ней содержится объект Dog. Animal здесь - это тип переменной, а не объекта.

среда, 25 декабря 2019 г.

Свободная JVM для не свободного ПО

#java #jvm #steam #openjdk


Имеется игра для андроида написанная на Java. Есть рабочий порт для винды и линукса.
Понятно что нужно использовать такие вещи как Launch4j. Понятно что не сложно написать
обертку для установочника и самому. Но не понятно, где взять свободную JVM, которую
можно было бы без мучений совести вкрутить в установочник, и продавать все это дело,
например на стиме, или гоге.
С лицензиями ХотСпота ничего не понял, не говоря уже о оракловских.
    


Ответы

Ответ 1



Вы можете свободно распространять билды OpenJDK вместе со своим коммерческим ПО. UPDATE: Вам не нужно ничего "вынимать" из OpenJDK. Вы можете просто указать Launch4j на необходимость включить JRE в состав результирующего файла. OpenJDK лицензирована под GPLv2. Ограничения будут действовать на производное ПО только в том случае, если вы собираетесь на её основе сделать LevenbergJDK The copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. OpenJDK состоит из средств разработки и среды выполнения. Ни то, ни другое никак не ограничивают условия использования ПО скомпилированного этими средствами или запускаемого в этой среде. Вы же не думаете, что программы собранные с помощью GCC и запускаемые в Linux должны быть обязательно бесплатными, а собранные с помощью MSVC и запускаемые в Windows обязательно платными? P.S. А ещё лучше вместо Launch4j используйте родную утилиту jlink.

понедельник, 23 декабря 2019 г.

Mark-and-sweep алгоритм сборки мусора

#java #jvm #сборщик_мусора


Насколько я понимаю, есть два фундаментальных подхода к сборе мусора:


Copying collectors
Mark-and-sweep


Оба алгоритма описываются в этой статье. По второму алгоритму автор статьи пишет:


  
  Объекты аллоцируются в памяти
  Нужно запустить GC
  Приложение приостанавливается
  Сборщик проходится по дереву объектов, помечая живые объекты
  Сборщик проходится по всей памяти, находя все не отмеченные куски памяти, сохраняя
их в "free list"
  Когда новые объекты начинают аллоцироватся они аллоцируются в память доступную
в "free list"
  


Если я правильно понимаю, то объекты, которые необходимо удалить, переносятся в "free
list". Но где, собственно, происходит удаление этих объектов? Или они явно не удаляются,
а просто перезаписываются при создании новых объектов?
    


Ответы

Ответ 1



Насколько я понимаю, нет необходимости производить дополнительные операции по удалению объектов в памяти. JVM будет создавать новые объекты на месте старых. Статья о том, как производится создание и удаление объектов в C++ The delete operator does not actually delete anything. It simply returns the memory being pointed to back to the operating system. The operating system is then free to reassign that memory to another application (or to this application again later). Думаю в java аналогично, только там не операционная система, а jvm. (Не совсем уверен, буду рад, если кто поправит)

Ответ 2



Если объект перестал быть доступным по сильной ссылке (strong reference), то он может быть собран сборщиком мусора. Сборщик мусора может вызвать у этого объекта метод finalize. Данный метод, потенциально, может восстановить объект - сделать его доступным по сильной ссылке (strong reference). Сборщик помечает память как "свободная", какие-то дополнительные меры по очистке памяти сборщик мусора не производит. При создании нового объекта в момент инициализации будет произведена установка всех полей в значение по умолчанию. Если необходимо удалить секретную информацию (пароль, ключ и т.д.), то необходимо делать это самостоятельно и как можно раньше. Так как в случае дампа памяти она может стать доступна. Так же часть информации может быть скопирована в другое место кучи и очистить "старую" область будет уже невозможно.

суббота, 21 декабря 2019 г.

Почему нельзя единожды скомпилировать байт-код на конечной машине

#java #jvm


Что делает jvm? Учитывая все особенности среды в которой она работает (так как она
именно под нее написана), интерпретирует байт-код, то есть динамически превращает в
машинный код.

Так вот, почему нельзя единожды, при "установке" программы (то есть первом попадании)
на компьютер с помощью jvm скомпилировать байт-код в машинный код и дальше уже не использовать
jvm? Ускорение ведь будет серьезное и может чуток памяти высвободится за счет отсутствия
jvm. Ведь есть же JIT который делает то же самое, но во время выполнения программы
и лишь кусками.
    


Ответы

Ответ 1



Так вот, почему нельзя единожды, при "установке" программы (то есть первом попадании) на компьютер с помощью jvm скомпилировать байт-код в машинный код и дальше уже не использовать jvm? Потому что тогда нельзя будет использовать спекуляции, за счет которых JIT-скомпилированный код вполне легально может обгонять AOT-скомпилированный код. Например, у нас может быть метод со следующей сигнатурой: void push(Consumer consumer) В этом случае АОТ ничего не останется кроме того, чтобы использовать интерфейс Consumer и вычислять реально вызываемый метод в рантайме. Однако если JIT на момент компиляции видит, что метод был вызван пять тысяч раз с одной и той же имплементацией Consumer - он может отбросить все лишнее, вызывать напрямую заранее известный метод и поставить перед этим т.н. trap на случай, если в метод все-таки упадет другая имплементация, и его надо будет перекомпилировать. Также JIT позволяет эффективный инлайнинг кода, который также происходит на основе учета количества вызовов и размера метода. АОТ не может знать, какой участок будет горячим без помощи программиста (который, в свою очередь, может ошибаться), а JIT - вполне. Ведь есть же JIT который делает то же самое, но во время выполнения программы и лишь кусками. JIT не делает этого кусками, весь код рано или поздно будет скомпилирован в машинный, просто пока компилятор не отработал, работает интерпретатор. Благодаря этому мы и можем, например, получить ассемблерный листинг.

Ответ 2



Небольшое не очень профессиональное объяснение работы исполнителей языка. Существуют: Компиляторы. Интерпретаторы. И компилятора интерпретаторы, или интерпретаторы компилирующего Типа. Отбросим первые два, и обсудим третий. Компилятор интепретирующего типа, это по сути компилятор и интерпретатор два в одном, поимер такого исполнителя это JVM, после написания текста программы, JVM в первую очередь использует компилятор, который компилирует ваш текст в байт-код, при этом этот этап имеет ряд оптимизаций, например пустые циклы, не проходят в байт-код, также бывает такое, что компилятор транслирует текст вообще в чистый машинный код, благодаря этому, достигается такая сумасшедшая скорость. Но следующим этапом, будет исполнения этого байт-кода, и вот этим, уже занимается JIT интерпретатор, которые интерпретирует машинный и байт коды, выводя нам результат. Соответственно теперь вы сами должны понять, почему нельзя добиться единоразового исполнения — программа на половину, исполняется динамически, то есть, если байт код мы в принципе можем единожды скомпилировать, но интерпретировать байт-код нужно каждый раз уникально. Такая технология называется JIT интерпретаторы. И-то все что я здесь описал, это без учета многих других моментов, оптимизаций, хаков и технологий... Но насколько мне известно, из любого приложения на Java Например, можно добиться итогового исполняемого файла, другой вопрос будет ли удобно каждый раз добиваться исполняемого файла и зачем вам тогда вообще язык на JVM? Ведь плюсы таких языков объективно видны на серверах, например.

Ответ 3



Для классов стандартной библиотеки это уже делается начиная с Java 9. Есть мнение, что после всесторонней обкатки AOT-компилятор станет доступен и для пользовательских классов.

Ответ 4



почему нельзя единожды, при "установке" программы (то есть первом попадании) на компьютер с помощью jvm скомпилировать байт-код в машинный код и дальше уже не использовать jvm? Ответ очень простой, jvm - это программа которая уже скомпилирована в машинный код. jvm не может компилировать код вашей программы в машинный код при "установке" программы, так как не являтся компилятором. В составе JDK имеется компилятор с языка Java, который компилирует в байткод. Байткод не является машинным кодом, поэтому может выполняться только на jvm. Хотя, в природе были известны компиляторы с языка Java, такие как gcj, но они почему-то не очень популярны. Вот еще одна имплементация AOT, так называемая GraalVM. Представялет из себя подмену JVM другой виртуальной машиной. Компиляция AOT: Полученная программа не запускается на виртуальной машине Java HotSpot, но использует необходимые компоненты, такие как управление памятью, планирование потоков из другой реализации виртуальной машины под названием «Субстратная виртуальная машина». Субстрат VM написан на Java и скомпилирован в собственный исполняемый файл.