sun.misc.Unsafe가 존재하는 이유는 무엇이며 실제 환경에서 어떻게 사용할 수 있습니까? [닫은]


267

나는 다른 날에 sun.misc.Unsafe 패키지를 발견했고 그것이 할 수있는 것에 놀랐습니다.

물론 수업은 문서화되어 있지 않지만 수업을 사용해야 할 충분한 이유가 있는지 궁금합니다. 어떤 시나리오를 사용해야합니까? 실제 시나리오에서 어떻게 사용될 수 있습니까?

당신이 경우 또한, 필요를 뭔가가 아마 당신의 디자인으로 잘못 표시하지 않는 것이, 무엇입니까?

Java에 왜이 클래스가 포함됩니까?


7
: 당신이 그것을 사용하는 경우 JDK 개발자들은 현재 그것의 가치가 설문 조사 작성 5 분 복용, 자바 (9)의 공용 API로 가능한 변환이 API를 검토하고 surveymonkey.com/s/sun-misc-Unsafe을 .
Andy Lynch

답변:


159

  1. VM "내재화" 즉, 잠금이없는 해시 테이블에 사용 된 CAS (Compare-And-Swap) 예 : sun.misc.Unsafe.compareAndSwapInt CAS에 대한 특별 지침이 포함 된 실제 JNI 호출을 기본 코드로 만들 수 있습니다.

    CAS에 대한 자세한 내용은 여기 ( http://en.wikipedia.org/wiki/Compare-and-swap)를 참조하십시오 .

  2. 호스트 VM의 sun.misc.Unsafe 기능을 사용하여 초기화되지 않은 객체를 할당 한 다음 생성자 호출을 다른 메소드 호출로 해석 할 수 있습니다.

  3. 기본 주소에서 데이터를 추적 할 수 있습니다. java.lang.Unsafe 클래스를 사용하여 객체의 메모리 주소를 검색하고 안전하지 않은 get / put 메소드를 통해 필드에서 직접 조작 할 수 있습니다!

  4. JVM에 대한 컴파일 시간 최적화 "매직"을 사용하는 고성능 VM. 낮은 수준의 작업이 필요합니다. 예 : http://en.wikipedia.org/wiki/Jikes_RVM

  5. 메모리 할당 sun.misc.Unsafe.allocateMemory 예 :-ByteBuffer.allocateDirect가 호출 될 때 DirectByteBuffer 생성자가 내부적으로이를 호출합니다.

  6. 호출 스택 추적 및 sun.misc로 인스턴스화 된 값으로 재생

  7. sun.misc.Unsafe.arrayBaseOffset 및 arrayIndexScale을 사용하여 대형 객체의 실시간 스캔, 업데이트 또는 이동 작업 비용을 제한하기 위해 대형 어레이를 더 작은 객체로 효율적으로 분할하는 기술인 arraylet을 개발할 수 있습니다.

  8. http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java

여기 참조에 대한 자세한 내용 -http : //bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html


1
안전하지 않음을 사용하여 필드의 주소를 얻는 경우 항상 GC에 의해 변경 될 수 있으므로 그 작업이 쓸모가 없습니까?
pdeva

당신이 할당 한 사람들의 주소를 얻으십시오
zudokod

내가 할당 한 것이 정확히 무엇을 의미합니까? 이것은 'new'연산자를 사용하여 객체를 만든 곳에서 사용되는 것 같습니다.
pdeva

1
unsafe.allocateMemory 값을 입력하십시오
zudokod

1
포인트 2와 관련하여 다른 메소드 호출로 생성자를 어떻게 호출 할 수 있는지 알고 싶습니다. 바이트 코드가 아닌 한 그렇게하는 방법을 찾지 못했기 때문입니다.
Miguel Gamboa

31

일부 코드 검색 엔진에서 검색 을 실행 하면 다음 예제가 표시됩니다.

{@link Unsafe} 객체에 액세스 할 수있는 간단한 클래스입니다. 어레이에서 효율적인 CAS 작업을 수행하려면 {@link Unsafe} *가 필요합니다. {@link java.util.concurrent.atomic.AtomicLongArray}와 같이 {@link java.util.concurrent.atomic}의 버전에는 일반적으로 이러한 알고리즘에 필요하지 않으며 비용이 많이 드는 추가 메모리 순서 보증이 필요합니다. 대부분의 프로세서에서.

  • SoyLatte -OSX 용 Java 6 Javadoc 발췌

/ ** sun.misc의 기본 클래스 정적 필드의 안전하지 않은 FieldAccessors 리플렉션 코드의 관점에서 볼 때 8 가지 기본 유형과 Object라는 9 가지 유형의 필드 만 관찰됩니다. 생성 된 바이트 코드 대신 Unsafe 클래스를 사용하면 동적으로 생성 된 FieldAccessors의 메모리 및로드 시간이 절약됩니다. * /

  • 스파이크 소스

/ * 와이어를 통해 전송되는 FinalFields .. 수신 측에서 개체를 비 정렬 화하고 다시 만드는 방법? 최종 필드에 대한 값을 설정하므로 생성자를 호출하고 싶지 않습니다. 발신자 쪽과 마찬가지로 최종 필드를 다시 만들어야합니다. 안전하지 않은 일. * /

다른 많은 예제가 있습니다. 위의 링크를 따르십시오 ...


25

흥미롭게도, 나는이 수업에 대해 들어 본 적이 없습니다.

한 가지 중요한 점 은 Unsafe # setMemory 를 사용하여 중요한 정보가 포함 된 버퍼 (암호, 키 등)를 0으로 만드는 것입니다. "불변의"객체의 필드에도이 작업을 수행 할 수 있습니다 (그런데 평범한 오래된 리플렉션도 여기서 트릭을 수행 할 수 있습니다). 나는 보안 전문가가 아니므로 소금 한 덩어리로 이것을 가져 가십시오.


4
I'd never even heard of this class... 나는 당신에게 그것에 대해 너무 많이 이야기했습니다! 한숨 + :(
Tim Bender

7
Java는 복사 세대 가비지 수집기를 사용하고 중요한 정보는 이미 '무료'메모리의 다른 곳에 덮어 쓰기를 기다리고 있기 때문에 아무런 의미가 없습니다.
Daniel 캐시디

39
나는 그것을들은 적이 없지만 그들의 park()문서를 좋아한다 . "현재 스레드 차단, 밸런싱 언 파크가 발생하거나 밸런싱 언 파크가 이미 발생했을 때 리턴, 또는 스레드가 중단되었거나 절대적이지 않고 시간이 0이 아닌 경우 주어진 시간 나노초가 경과했거나, 절대적이라면, Epoch가 경과 한 후 또는 예정대로 (즉, '이유'없이 돌아 오는) 지정된 마감 시간 (밀리 초 ) 입니다. "프로그램이 종료 될 때 메모리가 해제되거나 임의의 간격 중 먼저 오는 것"만큼 좋습니다.
aroth

1
@Daniel, 흥미 롭습니다. 나는 그것을 고려하지 않았습니다. 이제 내가 보안 전문가가 아닌 이유를 알 수 있습니다. :)
Mike Daniels

22

참조 추적에 이클립스를 사용하는 Java 1.6.12 라이브러리에 대한 매우 간단한 분석을 기반으로, 모든 유용한 기능이 Unsafe유용한 방식으로 노출되는 것처럼 보입니다 .

CAS 작업은 Atomic * 클래스를 통해 노출됩니다. 메모리 조작 기능은 DirectByteBuffer Sync 명령어 (park, unpark)를 통해 노출되며 AbstractQueuedSynchronizer를 통해 노출되며 이는 Lock 구현에서 사용됩니다.


AtomicXXXUpdater는 너무 느리고 실제로 필요할 때 : CAS-실제로 사용할 수는 없습니다. 금속을 사용하려는 경우 추상화 수준과 수많은 검사를 사용하지 않습니다. CAS 실패는 루프 esp에서 좋지 않습니다. 하드웨어가 높은 경합으로 인해 분기를 잘못 예측하기로 결정했지만 비교 / 분기가 더 적은 것은 단지 아프다. Park / Unpark는 LockSupportAQS를 통해 노출 되지 않습니다 (후자는 Park / unpark보다 잠금 장치에 더
가깝습니다

21

Unsafe.throwException- 확인 된 예외를 선언하지 않고 던질 수 있습니다.

리플렉션 또는 AOP를 처리하는 경우에 유용합니다.

사용자 정의 인터페이스에 대한 일반 프록시를 작성한다고 가정하십시오. 또한 사용자는 인터페이스에서 예외를 선언하기 만하면 특수한 경우에 함침에 의해 발생하는 예외를 지정할 수 있습니다. 그런 다음 이것이 인터페이스의 동적 구현에서 확인 된 예외를 발생시키는 유일한 방법입니다.

import org.junit.Test;
/** need to allow forbidden references! */ import sun.misc.Unsafe;

/**
 * Demonstrate how to throw an undeclared checked exception.
 * This is a hack, because it uses the forbidden Class {@link sun.misc.Unsafe}.
 */
public class ExceptionTest {

    /**
     * A checked exception.
     */
    public static class MyException extends Exception {
        private static final long serialVersionUID = 5960664994726581924L;
    }

    /**
     * Throw the Exception.
     */
    @SuppressWarnings("restriction")
    public static void throwUndeclared() {
        getUnsafe().throwException(new MyException());
    }

    /**
     * Return an instance of {@link sun.misc.Unsafe}.
     * @return THE instance
     */
    @SuppressWarnings("restriction")
    private static Unsafe getUnsafe() {
        try {

            Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
            singleoneInstanceField.setAccessible(true);
            return (Unsafe) singleoneInstanceField.get(null);

        } catch (IllegalArgumentException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (SecurityException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (NoSuchFieldException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (IllegalAccessException e) {
            throw createExceptionForObtainingUnsafe(e);
        }
    }

    private static RuntimeException createExceptionForObtainingUnsafe(final Throwable cause) {
        return new RuntimeException("error while obtaining sun.misc.Unsafe", cause);
    }


    /**
     * scenario: test that an CheckedException {@link MyException} can be thrown
     * from an method that not declare it.
     */
    @Test(expected = MyException.class)
    public void testUnsingUnsaveToThrowCheckedException() {
        throwUndeclared();
    }
}

14
Thread.stop(Throwable)안전하지 않아도 동일한 작업을 수행 할 수 있습니다 . 동일한 스레드에서 어쨌든 무엇이든 던질 수 있습니다 (컴파일 체크 없음)
bestsss

바이트 코드를 통해이 작업을 수행 할 수 있습니다 (또는 Lomboc을 사용하여 수행)
Antimony

1
@bestsss이 메소드는 UnsupportedOperationExceptionJava 8에서 스텁 아웃되어 현재 스레드에서 throw 됩니다. 그러나 인수가없는 인수 버전은 ThreadDeath여전히 작동합니다.
gparyani

@ damryfbfnetsi, 나는 꽤 오랫동안 핵심 jdk 토론을 따르지 않았고 Java 8로 이동할 계획이 없었습니다. 그러나 이것은 바이트 코드 생성으로 구현하기가 쉽지 않기 때문에 당혹 스럽습니다. 메소드는 throwables를 선언하지만 throw 된 예외에 대한 메타 데이터를 자유롭게 버릴 수 있으므로 이전 버전과 호환되지 않을 수 있습니다.
bestsss

10

안전하지 않은 클래스

안전하지 않은 저수준 작업을 수행하기위한 방법 모음. 클래스와 모든 메소드는 공용이지만이 클래스의 사용은 신뢰할 수있는 코드 만 인스턴스를 얻을 수 있으므로 제한됩니다.

그것을 사용하는 java.util.concurrent.atomic수업 중 하나입니다 :


6

효율적인 메모리 복사 (짧은 블록의 경우 System.arraycopy ()보다 복사 속도가 빠름); Java LZFSnappy 코덱에서 사용됩니다 . 바이트 단위로 복사하는 것보다 빠른 'getLong'및 'putLong'을 사용합니다. 16/32/64 바이트 블록과 같은 것을 복사 할 때 특히 효율적입니다.


1
Doh, arraycopy는 x86-64에서 SSE 루프를 사용하여보다 나은 getLong/putLong(및 주소도 계산해야 함)
bestsss

실제로 이것을 측정 했습니까? 짧은 블록의 경우 getLong/ 조합을 사용할 때 x86-64에서 지속적으로 더 나은 성능을 볼 수 있습니다 putLong. 이상적으로 System.arraycopy()는 단순함과 모두를 선호 합니다. 그러나 실제 테스트는 내가 테스트 한 경우에 대해 다르게 나타났습니다.
StaxMan

안전하지 않은 것을 사용하면 deflate impl에서 의미있는 성능을 얻을 수 없었습니다. 컴파일러에서 길이를 확인해야 할 때 큰 배열의 get / putLong은 몇 바이트에 걸쳐 긴 사본으로 작동 할 수 있습니다. 일부 impl. System.arrayCopy 과거에 메모리 펜스를 추가하십시오 (비활성화 / 활성화 가능). 그러면 진짜 범인이 될 수 있습니다.
bestsss 2016 년

확인. 최신 JDK가이를 변경했을 수 있습니다. 원래 (JDK 1.6 사용) 빠른 작동을 보았을 때도 놀랐습니다. 또는 사용법의 특정 차이점을 잊어 버릴 수도 있습니다. 이것들은 작동하는 경우에도 까다 롭고 불안정한 최적화이며 효과를 측정하는 것이 필수적입니다.
StaxMan

5

나는 최근에 JVM을 재 구현하는 작업을하고 있었고 놀라운 클래스가로 구현되었다는 것을 발견했다 Unsafe. 이 클래스는 주로 Java 라이브러리 구현자를 위해 설계되었으며 기본적으로 안전하지는 않지만 빠른 기본 요소를 빌드하는 데 필요한 기능을 포함합니다. 예를 들어, 하드웨어 수준 동기화, 메모리 할당 및 해제 등을 사용하여 원시 필드 오프셋을 가져오고 쓰는 방법이 있습니다. 일반적인 Java 프로그래머가 사용하지는 않습니다. 문서화되지 않고 구현에 따라 다르며 본질적으로 안전하지 않습니다 (따라서 이름!). 또한 SecurityManager거의 모든 경우에 액세스 할 수 없다고 생각합니다 .

간단히 말해서, 기본적으로 라이브러리 구현자가 기본 클래스와 같은 특정 클래스에서 모든 메소드를 선언하지 않고도 기본 시스템에 액세스 할 수 있도록합니다 AtomicInteger. 일반적인 Java 프로그래밍에서는이를 사용하거나 걱정할 필요가 없습니다. 요점은 그러한 종류의 액세스가 필요하지 않도록 나머지 라이브러리를 충분히 빠르게 만드는 것입니다.


실제로 보안 관리자는 리플렉션이 비활성화 된 경우에만 액세스를 허용하지 않습니다.
amara

@ sparkleshy- 이것에 대해 자세히 설명해 주시겠습니까?
templatetypedef

getUnsafe에서 인스턴스를 가져 오는 데는 엄격한 요구 사항 Unsafe.class.getDeclaredField("theUnsafe").setAccessible(true)있으며 그 요구 사항 .get(null)도 있습니다.
amara

@ sparkleshy- 나는 그것이 작동한다는 것에 놀랐습니다-보안 관리자가 그것을 표시해야합니다.
templatetypedef

5

자신의 복셀 엔진과 같이 대량의 메모리에 효율적으로 액세스하고 할당하는 데 사용하십시오! (예 : 마인 크래프트 스타일 게임)

내 경험상 JVM은 종종 필요한 곳에서 경계 검사를 제거 할 수 없습니다. 예를 들어, 큰 배열을 반복하고 있지만 실제 메모리 액세스가 루프의 비가 상 * 메소드 호출 아래에 고정되어있는 경우 JVM이 각 배열 액세스에 대해 한 번이 아니라 한 번만 경계 검사를 수행 할 수 있습니다. 루프. 따라서 잠재적으로 큰 성능 향상을 위해 sun.misc.Unsafe를 사용하여 메모리에 직접 액세스하는 방법을 통해 루프 내에서 JVM 경계 검사를 제거하여 올바른 위치에서 경계 검사를 수행 할 수 있습니다. (당신은 있는 거야 경계를 잘, 어떤 수준에서 확인?)
* 비 가상적으로, 클래스 / 메소드 / 인스턴스가 정적 / 최종 / 무엇의 조합인지 정확하게 보장했기 때문에 JVM이 특정 메소드가 무엇이든 동적으로 해결할 필요가 없음을 의미합니다.

자체 개발 한 복셀 엔진의 경우 청크 생성 및 직렬화 (한 번에 전체 어레이를 읽거나 쓰는 위치) 중에 성능이 크게 향상되었습니다. 결과가 다를 수 있지만 경계 제거 부족이 문제인 경우 문제가 해결됩니다.

이 문제에는 잠재적으로 중대한 문제가 있습니다. 특히 인터페이스의 클라이언트에 대한 경계 검사없이 메모리에 액세스 할 수있는 기능을 제공하면 메모리를 남용 할 수 있습니다. 해커도 인터페이스의 클라이언트가 될 수 있음을 잊지 마십시오. 특히 Java로 작성된 복셀 엔진의 경우에는 메모리 액세스가 남용되지 않도록 인터페이스를 설계해야합니다. 당신은 이제까지 전에 사용자 데이터의 유효성을 검사하기가 매우 조심해야한다 이제까지 당신의 위험한 인터페이스 어울려. 해커가 확인되지 않은 메모리 액세스로 할 수있는 치명적인 일을 고려할 때 두 가지 방법을 모두 사용하는 것이 가장 좋습니다.


4

힙 외부 수집은 대량의 메모리를 할당하고 사용 후 GC 간섭없이 즉시 할당을 해제하는 데 유용 할 수 있습니다. 을 기반으로 오프 힙 배열 / 목록 작업을위한 라이브러리 를 작성했습니다 sun.misc.Unsafe.


4

Unsafe를 사용하여 Arrays, HashMaps, TreeMaps와 같은 거대한 컬렉션을 구현했습니다.
그리고 조각화를 피 / 최소화하기 위해 안전하지 않은 dlmalloc 개념을 사용하여 메모리 할당자를 구현했습니다 .
이것은 우리가 동시성에서 성능을 얻는 데 도움이되었습니다.


3

Unsafe.park()그리고 Unsafe.unpark()맞춤형 동시성 제어 구조 및 협력 스케줄링 메커니즘의 구축을 위해.


24
공개적으로java.util.concurrent.locks.LockSupport
bestsss

1

직접 사용하지는 않았지만 때로는 두 개 이상의 스레드에서 읽히는 변수가 있으면 (실제로 휘발성으로 만들고 싶지는 않습니다) putObjectVolatile메인 스레드에서 쓸 때 사용할 수 있고 readObjectVolatile다른 스레드에서 드문 읽기를 수행 할 때.


1
그러나 아래 스레드에 대한 논의에 따르면, 만족하지 않은 휘발성은 어쨌든 비 휘발성만큼이나 빠릅니다. stackoverflow.com/questions/5573782/…
pdeva

휘발성 시맨틱을 일반 쓰기 및 휘발성 읽기로 바꿀 수는 없습니다. 이는 한 설정에서는 작동하지만 다른 설정에서는 작동하지 않을 수 있으므로 재난을위한 레시피입니다. 단일 작성기 스레드로 휘발성 의미론을 원한다면 쓰기 스레드에서 AtomicReference.lazySet을 사용하고 독자에서 get ()을 사용할 수 있습니다 (이 주제에 대한 설명 은이 게시물 참조 ). 휘발성 판독 값은 비교적 저렴하지만 무료는 아닙니다 . 여기를 참조 하십시오 .
Nitsan Wakart

"... 쓰면 putObjectVolatile을 사용할 수 있습니다 ..."나는 평범한 쓰기를 제안하지 않았습니다.
Matt Crinklaw-Vogt

1

현재이를 사용하는 클래스 중 하나에서 제공하는 기능을 교체해야하는 경우 필요합니다.

이것은 커스텀 / 빠른 / 더 콤팩트 한 직렬화 / 직렬화, ByteBuffer의 더 빠르거나 큰 버퍼 / 크기 조정 가능 버전 또는 현재 지원되지 않는 원자 변수를 추가하는 것일 수 있습니다.

나는 한 번에이 모든 것을 위해 그것을 사용했다.



0

객체는 Java 코드가 일반적으로 허용하는 것보다 낮은 수준에서 작동 할 수있는 것으로 보입니다. 고급 응용 프로그램을 코딩하는 경우 JVM은 메모리 처리 및 기타 작업을 코드 수준에서 추상화하여 프로그래밍하기 쉽습니다. 안전하지 않은 라이브러리를 사용하면 일반적으로 수행되는 하위 수준 작업을 효과적으로 완료 할 수 있습니다.

woliveirajr가 언급했듯이 "random ()"은 Unsafe를 사용하여 다른 많은 작업이 Unsafe에 포함 된 assignMemory () 함수를 사용하는 것처럼 시드합니다.

프로그래머는 아마도이 라이브러리를 필요로하지 않지만 저수준 요소를 엄격하게 제어 할 수있어 편리 할 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.