Java에서 스레드의 CPU/메모리 사용량을 조절합니까?
-
05-07-2019 - |
문제
저는 여러 스레드가 실행되는 애플리케이션을 작성 중이며 해당 스레드의 CPU/메모리 사용량을 제한하려고 합니다.
이있다 C++에 대한 비슷한 질문, 하지만 가능하다면 C++와 JNI를 사용하지 않으려고 합니다.더 높은 수준의 언어를 사용하면 이것이 불가능할 수도 있다는 것을 알고 있지만 누군가 아이디어가 있는지 궁금합니다.
편집하다: 현상금이 추가되었습니다.나는 이것에 대해 정말 훌륭하고 잘 생각된 아이디어를 원합니다.
편집 2: 이것이 필요한 상황은 내 서버에서 다른 사람의 코드를 실행하는 것입니다.기본적으로 이는 완전히 임의의 코드이며 클래스 파일에 기본 메서드가 있다는 것만 보장됩니다.현재 런타임 시 로드되는 완전히 다른 여러 클래스가 별도의 스레드로 동시에 실행되고 있습니다.
작성된 방식에 따르면, 실행되는 각 클래스에 대해 별도의 프로세스를 생성하기 위해 리팩토링하는 것은 어려울 것입니다.이것이 VM 인수를 통해 메모리 사용량을 제한하는 유일한 좋은 방법이라면 그렇게 하십시오.하지만 스레드를 사용하여 이를 수행할 수 있는 방법이 있는지 알고 싶습니다.별도의 프로세스이더라도 앞서 언급한 것처럼 여러 프로세스가 동시에 실행되므로 CPU 사용량을 어떻게든 제한할 수 있기를 바랍니다.나는 모든 리소스를 소모하는 무한 루프를 원하지 않습니다.
편집 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();
}
}
다른 팁
다음을 통해 CPU 및 메모리 사용량에 대한 많은 정보를 얻을 수 있습니다. JMX, 하지만 적극적인 조작을 허용하지는 않는다고 생각합니다.
CPU 사용량을 어느 정도 제어하려면 다음을 사용할 수 있습니다. Thread.set우선순위().
메모리의 경우 스레드별 메모리 같은 것이 없습니다.Java 스레드의 개념 자체가 공유 메모리를 의미합니다.메모리 사용량을 제어하는 유일한 방법은 -Xmx와 같은 명령줄 옵션을 사용하는 것이지만 런타임에 설정을 조작할 수 있는 방법은 없습니다.
돌보는 자바 포럼. 기본적으로 실행시기를 한 다음 너무 많은 시간이 걸릴 때 대기합니다. 원래 스레드에서 언급 된 바와 같이, 이것을 별도의 스레드로 실행하고 작업 스레드를 방해하면 시간이 지남에 따라 평균 값을 평균화 할 수 있으므로보다 정확한 결과를 얻을 수 있습니다.
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.
즉, 첫 번째 스레드가 나머지는 "더 자주"실행됩니다.
별도의 프로세스에서 스레드를 실행하면 메모리 사용량을 제한하고 CPU 수를 제한하거나 이러한 스레드의 우선 순위를 변경할 수 있습니다.
그러나 당신이하는 일은 종종 비생산적인 오버 헤드와 복잡성을 추가 할 가능성이 높습니다.
왜이 일을하고 싶은지 설명 할 수 없다면 (예 : 신뢰할 수없고 지원할 수없는 잘못 쓰여진 도서관이 있습니다) 나는 당신이 필요하지 않을 것을 제안 할 것입니다.
메모리 사용을 제한하기 쉽지 않은 이유는 공유 된 힙만이 하나만 있기 때문입니다. 따라서 한 스레드에서 사용되는 객체는 다른 스레드에서 사용할 수 있으며 한 스레드 또는 다른 스레드에 할당되지 않습니다.
CPU 사용을 제한한다는 것은 모든 스레드를 중지하여 아무것도하지 않도록하는 것을 의미하지만 더 나은 접근 방식은 스레드가 CPU를 낭비하지 않고 수행 해야하는 작업 만 수행하는 것입니다. 그들이하는 것을 멈추고 싶다.
"스레딩"을하는 대신 협력 멀티 태스킹을하는 대신 왜 조작 할 수 있는지 보는 것이 흥미로울 것입니다. http://www.janino.net/ 일정 시간/intstructions 세트 동안 프로그램을 실행하려면 다음 프로그램을 중지하고 실행하십시오. 적어도 그렇게 공정하게, 모든 사람에게 동시에 슬라이스를 줘 ...
Thread.setPriority ()는 도움이 될 수 있지만 스레드에서 사용하는 CPU를 캡처 할 수는 없습니다. 사실, 나는 이것을하는 Java 라이브러리에 대해 들어 본 적이 없습니다.
스레드가 협력 할 준비가되어 있으면 이러한 시설을 구현할 수 있습니다. 키는 스레드가 정기적으로 사용자 정의 스케줄러로 호출하고 JMX를 사용하여 스케줄러 모니터 스레드 CPU 사용을하도록하는 것입니다. 그러나 문제는 일부 스레드가 스케줄러 호출을 충분히 충분히 만들지 않으면 스로틀링 한계를 초과 할 수 있다는 것입니다. 그리고 루프에 갇히는 스레드에 대해 할 수있는 일은 없습니다.
구현하는 또 다른 이론적 경로는 분리 물을 사용하는 것입니다. 불행히도, 당신은 격리를 구현하는 범용 JVM을 찾기가 어려울 것입니다. 게다가, 표준 API를 사용하면 분리 물 내의 스레드가 아니라 분리 물을 제어 할 수 있습니다.
스레드 CPU 사용을 제한 할 수있는 유일한 방법은 리소스의 블록 또는 수율 ()를 자주 호출하는 것입니다.
이것은 CPU 사용을 100% 미만으로 제한하지 않지만 다른 스레드와 프로세스 더 많은 시간 경과를 제공합니다.
CPU를 줄이려면 Common 내부의 실을 잠들고 싶습니다. 만약에 그리고 동안 루프.
while(whatever) {
//do something
//Note the capitol 'T' here, this sleeps the current thread.
Thread.sleep(someNumberOfMilliSeconds);
}
수백 밀리 초 동안 자면 성능에 대한 눈에 띄는 결과가 거의 없거나 전혀없는 CPU 사용량을 크게 줄입니다.
메모리는 개별 스레드에서 프로파일 러를 실행하고 성능 튜닝을 수행합니다. 스레드에 사용할 수있는 메모리의 양을 조절하면 메모리 외 예외 또는 굶주린 스레드가있을 가능성이 높습니다. 나는 JVM이 필요한 스레드만큼 많은 메모리를 제공한다고 믿고 주어진 시간에 필수 객체 만 범위를 유지함으로써 메모리 사용을 줄이기 위해 노력할 것입니다.