Регулирование использования процессора / памяти потоком в Java?

StackOverflow https://stackoverflow.com/questions/1202184

Вопрос

Я пишу приложение, в котором будет запущено несколько потоков, и хочу ограничить использование процессора / памяти этими потоками.

Существует аналогичный вопрос для C ++, но я хочу попытаться избежать использования C ++ и JNI, если это возможно.Я понимаю, что это может быть невозможно при использовании языка более высокого уровня, но мне любопытно посмотреть, есть ли у кого-нибудь какие-нибудь идеи.

Редактировать: Добавлено вознаграждение;Мне бы хотелось получить несколько действительно хороших, хорошо продуманных идей по этому поводу.

ПРАВКА 2: Ситуация, для которой мне это нужно, заключается в выполнении чужого кода на моем сервере.По сути, это полностью произвольный код, с единственной гарантией того, что в файле класса будет метод main.В настоящее время несколько совершенно несопоставимых классов, которые загружаются во время выполнения, выполняются одновременно как отдельные потоки.

Судя по тому, как это написано, рефакторингу было бы непросто создавать отдельные процессы для каждого выполняемого класса.Если это единственный хороший способ ограничить использование памяти с помощью аргументов виртуальной машины, то пусть будет так.Но я хотел бы знать, есть ли способ сделать это с помощью потоков.Даже как отдельный процесс, я хотел бы иметь возможность каким-то образом ограничить использование его процессора, поскольку, как я упоминал ранее, несколько из них будут выполняться одновременно.Я не хочу, чтобы бесконечный цикл поглощал все ресурсы.

ПРАВКА 3: Самый простой способ приблизить размер объекта - использовать java Контрольно-измерительные приборы классы;в частности, метод getObjectSize.Обратите внимание, что для использования этого инструмента необходимы некоторые специальные настройки.

Это было полезно?

Решение

Если я понимаю вашу проблему, одним из способов было бы адаптивно перевести потоки в спящий режим, аналогично тому, как воспроизведение видео осуществляется в Java.Если вы знаете, что хотите использовать ядро на 50%, ваш алгоритм должен находиться в режиме ожидания примерно 0,5 секунды - потенциально распределенный в течение секунды (напримервычисление за 0,25 секунды, сон за 0,25 секунды и т.д.).Вот пример пример из моего видеоплеера.

long starttime = 0; // variable declared
//...
// for the first time, remember the timestamp
if (frameCount == 0) {
    starttime = System.currentTimeMillis();
}
// the next timestamp we want to wake up
starttime += (1000.0 / fps);
// Wait until the desired next time arrives using nanosecond
// accuracy timer (wait(time) isn't accurate enough on most platforms) 
LockSupport.parkNanos((long)(Math.max(0, 
    starttime - System.currentTimeMillis()) * 1000000));

Этот код будет переходить в режим ожидания в зависимости от значения кадры / секунда.

Чтобы ограничить использование памяти, вы могли бы обернуть создание вашего объекта в фабричный метод и использовать какой-нибудь семафор с ограниченными разрешениями в виде байтов, чтобы ограничить общий предполагаемый размер объекта (вам нужно оценить размер различных объектов, чтобы упорядочить семафор).

package concur;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class MemoryLimited {
    private static Semaphore semaphore = new Semaphore(1024 * 1024, true);
    // acquire method to get a size length array
    public static byte[] createArray(int size) throws InterruptedException {
        // ask the semaphore for the amount of memory
        semaphore.acquire(size);
        // if we get here we got the requested memory reserved
        return new byte[size];
    }
    public static void releaseArray(byte[] array) {
        // we don't need the memory of array, release
        semaphore.release(array.length);
    }
    // allocation size, if N > 1M then there will be mutual exclusion
    static final int N = 600000;
    // the test program
    public static void main(String[] args) {
        // create 2 threaded executor for the demonstration
        ExecutorService exec = Executors.newFixedThreadPool(2);
        // what we want to run for allocation testion
        Runnable run = new Runnable() {
            @Override
            public void run() {
                Random rnd = new Random();
                // do it 10 times to be sure we get the desired effect
                for (int i = 0; i < 10; i++) {
                    try {
                        // sleep randomly to achieve thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // ask for N bytes of memory
                        byte[] array = createArray(N);
                        // print current memory occupation log
                        System.out.printf("%s %d: %s (%d)%n",
                            Thread.currentThread().getName(),
                            System.currentTimeMillis(), array,
                            semaphore.availablePermits());
                        // wait some more for the next thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // release memory, no longer needed
                        releaseArray(array);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // run first task
        exec.submit(run);
        // run second task
        exec.submit(run);
        // let the executor exit when it has finished processing the runnables
        exec.shutdown();
    }
}

Другие советы

Вы можете получить много информации об использовании процессора и памяти с помощью JMX, но я не думаю, что это допускает какие-либо активные манипуляции.

Для контроля загрузки процессора в некоторой степени вы можете использовать Thread.setPriority().

Что касается памяти, то не существует такого понятия, как память для каждого потока.Сама концепция Java threads означает разделяемую память.Единственный способ контролировать использование памяти - это использовать параметры командной строки, такие как -Xmx, но нет способа манипулировать настройками во время выполнения.

Забота о Форумы Java.По сути, вы рассчитываете время выполнения, а затем ждете, когда это займет слишком много времени.Как упоминалось в исходном потоке, выполнение этого в отдельном потоке и прерывание рабочего потока даст более точные результаты, равно как и усреднение значений по времени.

import java.lang.management.*;

ThreadMXBean TMB = ManagementFactory.getThreadMXBean();
long time = new Date().getTime() * 1000000;
long cput = 0;
double cpuperc = -1;

while(true){

if( TMB.isThreadCpuTimeSupported() ){
    if(new Date().getTime() * 1000000 - time > 1000000000){ //Reset once per second
        time = new Date().getTime() * 1000000;
        cput = TMB.getCurrentThreadCpuTime();
    }

    if(!TMB.isThreadCpuTimeEnabled()){
        TMB.setThreadCpuTimeEnabled(true);
    }

    if(new Date().getTime() * 1000000 - time != 0)
        cpuperc = (TMB.getCurrentThreadCpuTime() - cput) / (new Date().getTime() *  1000000.0 - time) * 100.0;                  
    }
//If cpu usage is greater then 50%
if(cpuperc > 50.0){
     //sleep for a little bit.
     continue;
}
//Do cpu intensive stuff
}

Вы можете назначить потокам разные приоритеты, чтобы наиболее релевантный поток планировался чаще.

Посмотри на это ответ чтобы посмотреть, поможет ли это.

Когда все запущенные потоки имеют одинаковый приоритет, они могут выполняться следующим образом:

t1, t2, t3,     t1, t2, t3,   t1, t2, t3

Когда вы присваиваете одному из них другой приоритет, это может выглядеть следующим образом:

t1, t1, t1, t1,    t2,    t1, t1, t1 t3.

То есть первый поток выполняется "чаще", чем остальные.

Если вы запускаете потоки в отдельном процессе, вы можете ограничить использование памяти и ограничить количество процессоров или изменить приоритет этих потоков.

Однако все, что вы делаете, скорее всего, приведет к дополнительным накладным расходам и сложности, которые часто контрпродуктивны.

Если вы не можете объяснить, почему вы хотели бы это сделать (напримеру вас плохо написанная библиотека, которой вы не доверяете и для которой не можете получить поддержку) Я бы предположил, что вам это не нужно.

Причина, по которой нелегко ограничить использование памяти, заключается в том, что существует только одна общая куча.Таким образом, объект, который используется в одном потоке, может использоваться в другом и не присваивается тому или иному потоку.

Ограничение использования процессора означает остановку всех потоков, чтобы они ничего не делали, однако лучший подход - убедиться, что поток не тратит впустую процессор и активен только при выполнении работы, которая должна быть выполнена, и в этом случае вы не хотели бы мешать им делать это.

Почему бы вместо выполнения "многопоточности" не заняться совместной многозадачностью, было бы интересно посмотреть, сможете ли вы манипулировать http://www.janino.net/ запустить программу в течение определенного периода времени / набора инструкций, затем остановиться и запустить следующую программу.По крайней мере, так будет справедливо - дать всем одинаковый временной отрезок...

Thread.setPriority() может помочь, но это не позволяет вам ограничить процессор, используемый потоком.На самом деле, я не слышал ни о какой библиотеке Java, которая делает это.

Возможно, удастся реализовать такое средство при условии, что ваши потоки готовы к сотрудничеству.Ключ в том, чтобы потоки периодически вызывали пользовательский планировщик и чтобы планировщик отслеживал загрузку процессора потоков с помощью JMX.Но проблема в том, что если какой-то поток недостаточно часто вызывает планировщик, он вполне может превысить пределы регулирования.И вы ничего не можете поделать с потоком, который застревает в цикле.

Другим теоретическим путем к реализации было бы использование изолятов.К сожалению, вам будет трудно найти JVM общего назначения, реализующую isolates.Кроме того, стандартные API позволяют вам управлять только изолятом, но не потоками внутри изолята.

Единственный способ ограничить использование процессора потоком - это либо заблокировать ресурс, либо часто вызывать yield().

Это не ограничивает загрузку процессора ниже 100%, но предоставляет другим потокам и процессам больше временных интервалов.

Чтобы уменьшить объем процессора, вы хотите перевести свои потоки в спящий режим внутри common если и в то время как петли.

while(whatever) {
    //do something
    //Note the capitol 'T' here, this sleeps the current thread.
    Thread.sleep(someNumberOfMilliSeconds);
}

Переход в режим ожидания на несколько сотен миллисекунд значительно сократит загрузку процессора, практически не отразившись на производительности.

Что касается памяти, я бы запустил профилировщик в отдельных потоках и немного настроил производительность.Если вы ограничили объем памяти, доступный потоку, я думаю, что вероятно исключение нехватки памяти или нехватка потока.Я бы доверил JVM предоставлять столько памяти, сколько необходимо потоку, и работать над сокращением использования памяти, сохраняя только важные объекты в области видимости в любой момент времени.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top