Pergunta

Eu estou escrevendo um aplicativo que terá várias threads em execução, e quer estrangular o uso da CPU / memória desses tópicos.

Há uma pergunta semelhante para C ++ , mas eu quero tentar e evitar o uso C ++ e JNI, se possível. Sei que isso pode não ser possível, utilizando uma linguagem de nível superior, mas estou curioso para ver se alguém tiver alguma idéia.

EDIT: Adicionado uma recompensa; Eu gostaria alguns realmente bom, bem pensado ideias sobre este assunto.

EDIT 2: A situação eu preciso disso para se executar o código de outras pessoas no meu servidor. Basicamente, é um código completamente arbitrária, com a única garantia é que haverá um método principal no arquivo de classe. Actualmente, várias classes completamente diferentes, que são carregados no tempo de execução, estão executando concorrentemente como segmentos separados.

A forma como está escrito, seria uma dor de refatorar para criar processos separados para cada classe que é executado. Se essa é a única boa maneira de uso de memória limite através dos argumentos VM, então que assim seja. Mas eu gostaria de saber se há uma maneira de fazê-lo com threads. Mesmo como um processo separado, eu gostaria de ser capaz de limitar de alguma forma o uso da CPU, pois, como já referi anteriormente, vários destes será a execução de uma só vez. Eu não quero um loop infinito para monopolizar todos os recursos.

EDIT 3: Uma maneira fácil de tamanho aproximado objeto está com o java aulas noreferrer Instrumentação ; especificamente, o método getObjectSize. Note-se que há alguma configuração especial necessária para usar esta ferramenta.

Foi útil?

Solução

Se eu entender o seu problema, uma forma seria a de forma adaptativa sono dos fios, da mesma forma como a reprodução de vídeo é feito em Java. Se você sabe que quer 50% de utilização núcleo, o seu algoritmo deve dormir cerca de 0,5 segundos - potencialmente distribuídas dentro de um segundo (por exemplo 0,25 sec computação, 0,25 seg sono, e.t.c.). Aqui está um exemplo do meu player de vídeo.

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));

Este código vai dormir com base nos quadros / segundo valor.

Para acelerador o uso de memória, você poderia envolver sua criação objeto em um método de fábrica, e usar algum tipo de semáforo com permissões limitadas como bytes para limitar o tamanho total estimado do objeto (o que você precisa para estimar o tamanho de vários objetos para ração o semáforo).

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();
    }
}

Outras dicas

Você pode obter um monte de informações sobre o uso de CPU e memória através de JMX , mas eu não acho que ele permite que qualquer manipulação ativa.

Para controlar o uso da CPU até certo ponto, você pode usar Thread.setPriority () .

Quanto à memória, não existe tal coisa como memória por thread. O próprio conceito de Java Threads meio de memória compartilhada. A única maneira de uso de memória de controle é através das opções de linha de comando como -Xmx, mas não há nenhuma maneira de manipular as configurações em tempo de execução.

Care of Java Fóruns . Basicamente o seu timing de execução e, em seguida, à espera quando seu levando muito tempo. Como é mencionado no segmento original, executar este em um segmento separado e interrompendo o fio trabalho dará resultados mais precisos, como valores vontade de média ao longo do tempo.

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
}

Você pode atribuir diferentes prioridades para os tópicos de modo que o segmento mais relevante se agendado com mais freqüência.

Look at this resposta para ver se isso ajuda.

Quando todo o segmento em execução têm a mesma prioridade que pode ser executado como este:

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

Quando você atribuir uma prioridade diferente para um deles pode ser parecido:

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

Isto é, o primeiro segmento é executado "mais frequentemente" que o resto.

Se você executar os fios em um processo separado pode limitar o uso de memória e limitam o número de CPUs ou alterar a prioridade destes tópicos.

No entanto, qualquer coisa que você faz é provável adicionar sobrecarga ea complexidade que muitas vezes é contraproducente.

A menos que você pode explicar por que você iria querer fazer isso (por exemplo, você tem uma biblioteca de mal escrito que você não confia e não pode obter suporte para) eu sugiro que você não precisa.

A razão não é fácil para restringir o uso de memória é que há apenas um amontoado que é compartilhada. Portanto, um objeto que é usado em uma thread é utilizável em outro e não é atribuído a um segmento ou de outra.

A limitação de CPU meios de uso parando todos os fios para que eles não fazem nada, no entanto, uma abordagem melhor é certificar-se o fio não perca CPU e só estão activas fazendo o trabalho que precisa ser feito, no caso de você que não gostaria de detê-los a fazê-lo.

Por que não em vez de fazer "enfiar" fazer multitarefa cooperativa, seria interessante ver se você pode manipular http: / /www.janino.net/ para executar um programa para um determinado período de tempo / conjunto de intstructions, em seguida, parar e executar o próximo programa. Pelo menos assim o seu justo, dar a todos a mesma fatia de tempo ...

Thread.setPriority () pode ajudar, mas não permitem que você limitar o CPU usado por um fio. Na verdade, eu não ouvi falar de qualquer biblioteca Java que faz isso.

Pode ser possível implementar tal facilidade a condição de que seus tópicos estão dispostos a cooperar. A chave é ter os fios periodicamente põe em um agendador personalizado, e têm o uso do monitor Agendador de thread CPU usando JMX. Mas o problema é que, se alguma linha não fazer a chamada programador muitas vezes ele pode muito bem exceder os limites de estrangulamento. E não há nada que você possa fazer em relação a um segmento que fica preso em um loop.

Outra rota teórica para a implementação seria usar isolados. Infelizmente, você vai ter dificuldade para encontrar uma JVM de uso geral que implementa isolados. Além disso, as APIs padrão só permitem-lhe controlar o isolado, e não os tópicos dentro do isolado.

A única maneira você pode limitar o uso de CPU da linha é por qualquer bloco em um recurso ou rendimento call () freqüentemente.

Isso não limita o uso da CPU abaixo de 100%, mas dá outros segmentos e processos mais timeslices.

Para reduzir CPU, você quer dormir seus tópicos dentro de comum se e enquanto loops.

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

Sleeping por algumas centenas de milissegundos irá reduzir significativamente o uso da CPU com pouco ou nenhum resultado notável no desempenho.

Quanto à memória, eu executar um profiler sobre os tópicos individuais e fazer algum ajuste de desempenho. Se você fora estrangulado a quantidade de memória disponível para enfiar Eu acho que uma exceção de memória ou fio de fome é provável. Eu confiaria a JVM para fornecer o máximo de memória como o fio necessário e trabalhar na redução do uso de memória, mantendo apenas os objetos essenciais no âmbito a qualquer momento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top