fix(gui): reduce threads count on low memory, other tweaks (#1410)
This commit is contained in:
@@ -16,9 +16,14 @@ import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.ProcessState;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
|
||||
import static jadx.core.dex.nodes.ProcessState.GENERATED_AND_UNLOADED;
|
||||
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
|
||||
import static jadx.core.dex.nodes.ProcessState.PROCESS_COMPLETE;
|
||||
import static jadx.gui.utils.FileUtils.toFiles;
|
||||
|
||||
public class JadxWrapper {
|
||||
@@ -51,6 +56,15 @@ public class JadxWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check and move into core package
|
||||
public void unloadClasses() {
|
||||
for (ClassNode cls : decompiler.getRoot().getClasses()) {
|
||||
ProcessState clsState = cls.getState();
|
||||
cls.unload();
|
||||
cls.setState(clsState == PROCESS_COMPLETE ? GENERATED_AND_UNLOADED : NOT_LOADED);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
decompiler.close();
|
||||
|
||||
@@ -76,8 +76,8 @@ public class BackgroundExecutor {
|
||||
execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable), onFinishUiRunnable));
|
||||
}
|
||||
|
||||
public void execute(String title, Runnable backgroundRunnable) {
|
||||
execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable), null));
|
||||
public Future<TaskStatus> execute(String title, Runnable backgroundRunnable) {
|
||||
return execute(new SimpleTask(title, Collections.singletonList(backgroundRunnable), null));
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor makeTaskQueueExecutor() {
|
||||
@@ -86,6 +86,7 @@ public class BackgroundExecutor {
|
||||
|
||||
private final class TaskWorker extends SwingWorker<TaskStatus, Void> implements ITaskInfo {
|
||||
private final IBackgroundTask task;
|
||||
private ThreadPoolExecutor executor;
|
||||
private TaskStatus status = TaskStatus.WAIT;
|
||||
private long jobsCount;
|
||||
private long jobsComplete;
|
||||
@@ -117,7 +118,7 @@ public class BackgroundExecutor {
|
||||
task.getTitle(), jobsCount, task.timeLimit(), task.checkMemoryUsage());
|
||||
status = TaskStatus.STARTED;
|
||||
int threadsCount = mainWindow.getSettings().getThreadsCount();
|
||||
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
||||
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
||||
for (Runnable job : jobs) {
|
||||
executor.execute(job);
|
||||
}
|
||||
@@ -174,14 +175,22 @@ public class BackgroundExecutor {
|
||||
LOG.error("Task '{}' execution timeout, force cancel", task.getTitle());
|
||||
return TaskStatus.CANCEL_BY_TIMEOUT;
|
||||
}
|
||||
if (checkMemoryUsage && !UiUtils.isFreeMemoryAvailable()) {
|
||||
LOG.error("Task '{}' memory limit reached, force cancel", task.getTitle());
|
||||
return TaskStatus.CANCEL_BY_MEMORY;
|
||||
}
|
||||
if (isCancelled() || Thread.currentThread().isInterrupted()) {
|
||||
LOG.warn("Task '{}' canceled", task.getTitle());
|
||||
return TaskStatus.CANCEL_BY_USER;
|
||||
}
|
||||
if (checkMemoryUsage && !UiUtils.isFreeMemoryAvailable()) {
|
||||
LOG.info("Memory usage: {}", UiUtils.memoryInfo());
|
||||
if (executor.getCorePoolSize() == 1) {
|
||||
LOG.error("Task '{}' memory limit reached, force cancel", task.getTitle());
|
||||
return TaskStatus.CANCEL_BY_MEMORY;
|
||||
}
|
||||
LOG.warn("Low memory, reduce processing threads count to 1");
|
||||
// reduce thread count and continue
|
||||
executor.setCorePoolSize(1);
|
||||
System.gc();
|
||||
UiUtils.sleep(500); // wait GC
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ import ch.qos.logback.classic.Level;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.plugins.utils.CommonFileUtils;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
@@ -342,9 +343,7 @@ public class MainWindow extends JFrame {
|
||||
if (!ensureProjectIsSaved()) {
|
||||
return;
|
||||
}
|
||||
cancelBackgroundJobs();
|
||||
clearTree();
|
||||
wrapper.close();
|
||||
closeAll();
|
||||
updateProject(new JadxProject());
|
||||
}
|
||||
|
||||
@@ -406,16 +405,14 @@ public class MainWindow extends JFrame {
|
||||
void open(List<Path> paths, Runnable onFinish) {
|
||||
if (paths.size() == 1) {
|
||||
Path singleFile = paths.get(0);
|
||||
if (singleFile.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(JadxProject.PROJECT_EXTENSION)) {
|
||||
openProject(singleFile);
|
||||
onFinish.run();
|
||||
String fileExtension = CommonFileUtils.getFileExtension(singleFile.getFileName().toString());
|
||||
if (fileExtension != null && fileExtension.equalsIgnoreCase(JadxProject.PROJECT_EXTENSION)) {
|
||||
openProject(singleFile, onFinish);
|
||||
return;
|
||||
}
|
||||
}
|
||||
closeAll();
|
||||
project.setFilePath(paths);
|
||||
clearTree();
|
||||
BreakpointManager.saveAndExit();
|
||||
LogCollector.getInstance().reset();
|
||||
if (paths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -433,6 +430,17 @@ public class MainWindow extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
private void closeAll() {
|
||||
cancelBackgroundJobs();
|
||||
saveOpenTabs();
|
||||
clearTree();
|
||||
BreakpointManager.saveAndExit();
|
||||
LogCollector.getInstance().reset();
|
||||
wrapper.close();
|
||||
tabbedPane.closeAllTabs();
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private void checkLoadedStatus() {
|
||||
if (!wrapper.getClasses().isEmpty()) {
|
||||
return;
|
||||
@@ -483,7 +491,7 @@ public class MainWindow extends JFrame {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openProject(Path path) {
|
||||
private void openProject(Path path, Runnable onFinish) {
|
||||
if (!ensureProjectIsSaved()) {
|
||||
return;
|
||||
}
|
||||
@@ -500,9 +508,9 @@ public class MainWindow extends JFrame {
|
||||
settings.addRecentProject(path);
|
||||
List<Path> filePaths = jadxProject.getFilePaths();
|
||||
if (filePaths == null) {
|
||||
clearTree();
|
||||
closeAll();
|
||||
} else {
|
||||
open(filePaths);
|
||||
open(filePaths, onFinish);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,10 +569,14 @@ public class MainWindow extends JFrame {
|
||||
DecompileTask decompileTask = new DecompileTask(this, wrapper);
|
||||
backgroundExecutor.executeAndWait(decompileTask);
|
||||
|
||||
backgroundExecutor.execute(decompileTask.getTitle(), wrapper::unloadClasses).get();
|
||||
System.gc();
|
||||
|
||||
IndexTask indexTask = new IndexTask(this, wrapper);
|
||||
backgroundExecutor.executeAndWait(indexTask);
|
||||
|
||||
processDecompilationResults(decompileTask.getResult(), indexTask.getResult());
|
||||
System.gc();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Decompile task execution failed", e);
|
||||
}
|
||||
@@ -1395,7 +1407,9 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void saveOpenTabs() {
|
||||
project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex());
|
||||
if (project != null) {
|
||||
project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex());
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreOpenTabs() {
|
||||
|
||||
@@ -314,4 +314,12 @@ public class UiUtils {
|
||||
LOG.error("Failed copy text to clipboard", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sleep(int ms) {
|
||||
try {
|
||||
Thread.sleep(ms);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user