JVM을 어떻게 중단합니까?


144

저자가 인터뷰 대상자에게 "JVM을 어떻게 충돌 시키는가?"라는 프로그래밍 기술에 관한 책을 읽고있었습니다. 결국 모든 메모리를 사용하는 무한 for-loop를 작성하여 그렇게 할 수 있다고 생각했습니다.

누구든지 어떤 아이디어가 있습니까?



stackoverflow.com/questions/30072883 / ... "JDK1.8_40 이상을 사용하는 경우 (Oracle 또는 OpenJDK도 마찬가지) 대화 상자 크기 조정과 함께 다음 코드는 응용 프로그램을 중단시킵니다 (Windows 7, x64, 64 비트 JDK 시도)." -코드는 40 줄이므로 JVM 이 올바르게 충돌 합니다.
Dreamspace President

답변:


6

단일 "응답"에 가장 가까운 것은 System.exit()적절한 정리없이 즉시 JVM을 종료하는 것입니다. 그러나 그 외에도 네이티브 코드와 리소스 소모가 가장 가능성이 높은 답변입니다. 또는 JVM 버전에서 버그를 Sun의 버그 추적기에서 찾아 볼 수 있습니다.이 중 일부는 반복 가능한 충돌 시나리오를 허용합니다. 우리는 32 비트 버전에서 4Gb 메모리 제한에 접근 할 때 반 정기 충돌을 겪었습니다 (일반적으로 64 비트를 사용합니다).


71
적절한 정리없이? 확실합니까? 문서에 "종료 시퀀스를 시작하여 현재 실행중인 Java 가상 머신을 종료합니다 ... 등록 된 모든 종료 훅 (있는 경우)이 시작됩니다 ... 모든 호출되지 않은 종료자가 실행됩니다"-적절한 정리가 아닙니까?
user85421

51
이것은 JVM과 충돌하지 않으며, 의도적이고 명시 적으로 순서대로 실행 종료를 시작합니다.
BenM

9
jvm 충돌에 가장 가까운 것은 Runtime.getRuntime (). halt (status)입니다. 문서에 따르면 "이 방법은 종료 후크가 시작되지 않으며 종료시 종료가 활성화 된 경우 호출되지 않은 종료자를 실행하지 않습니다"라고합니다. 여전히 충돌은 아니지만 System.exit보다 가깝습니다.
henry April

48
이것은 실제로 나쁜 대답입니다.
안토니오

174

OutOfMemoryError 또는 StackOverflowError에 충돌을 발생시키는 호출을하지 않습니다. 이것들은 단지 일반적인 예외입니다. VM을 실제로 크래시하려면 다음 세 가지 방법이 있습니다.

  1. 기본 코드에서 JNI를 사용하여 충돌하십시오.
  2. 보안 관리자가 설치되어 있지 않으면 리플렉션을 사용하여 VM을 중단 할 수 있습니다. 이것은 VM마다 다르지만 일반적으로 VM은 기본 리소스에 대한 많은 포인터를 개인 필드에 저장합니다 (예 : 기본 스레드 객체에 대한 포인터는 java.lang.Thread 의 긴 필드에 저장 됨 ). 리플렉션을 통해 변경하면 VM이 조만간 충돌합니다.
  3. 모든 VM에는 버그가 있으므로 하나만 트리거하면됩니다.

마지막 방법으로 간단한 예를 들었습니다. Sun Hotspot VM이 조용히 충돌합니다.

public class Crash {
    public static void main(String[] args) {
        Object[] o = null;

        while (true) {
            o = new Object[] {o};
        }
    }
}

이로 인해 GC에서 스택 오버플로가 발생하므로 StackOverflowError가 발생하지 않지만 hs_err * 파일을 포함하여 실제 충돌이 발생합니다.


9
와! 이것은 hs_err * 파일이없고 "세그먼트 오류!"만으로 Sun Java 5, Sun Java 6 및 OpenJDK 6 (Ubuntu 9.04)과 충돌합니다. ...
Joachim Sauer

1
System.exit와 ()는 (보안 관리자가 설치되어 있지 않으면)는 JVM 충돌하는 훨씬 쉬운 방법입니다
instantsetsuna

4
언제 수정되었는지 모르지만 1.7.0_09에서 테스트했는데 괜찮습니다. 스레드 "main"의 예외 java.lang.OutOfMemoryError : CrashJVM.main (CrashJVM.java:7)의 Java 힙 공간
Chad NB

2
인텔 코어 i7 2.4GHz / 8GB RAM / JDK1.7 64 비트에서 위의 코드를 시도했지만 20 분 후에도 여전히 JVM이 작동합니다. (재미 : 랩탑 팬이 하늘의 전투기보다 더 컸습니다). 이 문제는 JDK 1.7 이상에서 해결 되었습니까?
realPK

3
JDK 1.8_u71에서이 문제는java.lang.OutOfMemoryError: GC overhead limit exceeded
Clashsoft


56

이것을 사용하십시오 :

import sun.misc.Unsafe;

public class Crash {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    public static void crash() {
        unsafe.putAddress(0, 0);
    }
    public static void main(String[] args) {
        crash();
    }
}

이 클래스는 신뢰할 수있는 코드를 사용하므로 부트 클래스 경로에 있어야합니다. 따라서 다음과 같이 실행하십시오.

java -Xbootclasspath / p :. 크래시


14
-Xbootclasspath를 사용하는 대신 다음과 같이 할 수도 있습니다 Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );.
pushy February

Unsafe정의상 "안전하지 않은"입니다. 이것은 약간의 속임수입니다.
핫 릭

1
사용 일 확정 getDeclaredField생산을 포함하여, 리눅스 64에서 JDK 8u131에 트릭을 hs_err_pid*.logA로부터 SIGSEGV.
Jesse Glick

사용 Unsafe은 속임수가 아닙니다. OP는 프로그래밍 문제에 대한 '깨끗한'솔루션을 찾고 있지 않습니다. 그는 가장 추악한 방법으로 jvm이 충돌해야합니다. 불쾌한 네이티브 일을하는 것이 그 일을 할 수있는 일이며, 정확히 그렇습니다 Unsafe.
jvdneste

34

Chad Fowler의 The Passionate Programmer 에서도이 질문에 부딪 쳤기 때문에 여기에 왔습니다 . 사본에 접근 할 수없는 사람들을 위해, 질문은 "정말로 훌륭한 자바 프로그래머"가 필요한 입장을 인터뷰하는 응시자들을위한 일종의 필터 / 테스트로 구성됩니다.

특히 그는 묻습니다.

순수 Java로 Java Virtual Machine이 충돌하는 프로그램을 어떻게 작성 하시겠습니까?

저는 15 년 이상 Java로 프로그래밍했으며이 질문은 당혹스럽고 불공평 한 것으로 나타났습니다. 다른 사람들이 지적했듯이 관리되는 언어 인 Java는 특별히 충돌하지 않도록 설계되었습니다 . 물론 JVM 버그는 항상 있지만

  1. 프로덕션 레벨 JRE가 15 년 이상 지난 후에는 드물다.
  2. 이러한 버그는 다음 릴리스에서 패치 될 가능성이 있으므로 프로그래머가 현재 JRE 쇼 스토퍼 세트의 세부 사항을 실행하고 기억할 가능성은 얼마나됩니까?

다른 사람들이 언급했듯이 JNI를 통한 일부 네이티브 코드는 JRE를 충돌시키는 확실한 방법입니다. 그러나 저자 는 순수 Java로 구체적으로 언급 했으므로 그 결과는 없습니다.

또 다른 옵션은 JRE 가짜 바이트 코드를 제공하는 것입니다. 가비지 이진 데이터를 .class 파일로 덤프하고 JRE에 실행하도록 요청하는 것이 쉽습니다.

$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap

그게 중요합니까? JRE 자체가 다운되지 않았 음을 의미합니다. 가짜 코드를 제대로 감지하여보고하고 종료했습니다.

이것은 재귀를 통해 스택을 날리는 것, 객체 할당을 통한 힙 메모리 부족 또는 단순히 던지기와 같은 가장 명백한 종류의 솔루션을 남깁니다 RuntimeException. 그러나 이로 인해 JRE가 StackOverflowError비슷한 예외 로 종료되어 실제로 충돌이 아닙니다 .

남은 것은 무엇입니까? 저자가 실제로 올바른 솔루션으로 생각한 것을 듣고 싶습니다.

업데이트 : Chad Fowler 가 여기에 응답했습니다 .

추신 : 그것은 다른 위대한 책입니다. 루비를 배우면서 도덕적 지원을 받기 위해 그것을 선택했습니다.


1
이러한 인터뷰 질문은 1) (많은) JVM 충돌을 목격하고 2) 결론을 내렸거나 (자신이 주장한) Java 전문가가 충돌의 근본적인 이유. Chad Fowler는 자신의 저서에서 자체 선언 한 Java 프로그래머는 "오답을 찾을 수 없었습니다"라고 말합니다. 즉, 전체 클래스의 잠재적 인 문제에 대해 생각하지 않았습니다. 결론 :이 질문은 "JVM 충돌을 방지하는 방법"에 대한 부분입니다. 확실히 알면 더 좋습니다.
Shonzilla

1
나는 JVM이나 단어 "Crash"를 실제로 이해하지 못하기 때문에 "스택 오버플로", system.exit () 또는 기타 "정상"종료에 응답하는 사람들을 찾을 것입니다. 이것이 매우 비정상적임을 인식하는 것은 고급 프로그래머를 식별하는 아주 좋은 방법입니다. 나는 당신의 첫 번째 진술에 동의합니다. 나는 묻고 요청하는 것이 훌륭하고 완전히 공정한 질문이라는 것을 알게 될 것입니다. 구체적인 답변이없는 것이 항상 최고입니다.
Bill K

20

이 코드는 불쾌한 방식으로 JVM을 중단시킵니다.

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}

1
이 코드와 함께 JDK 1.7을 사용할 때 컴파일 오류가 발생합니다. 액세스 제한 : 필수 라이브러리 C : \ Program Files \ Java \ jdk1.7.0_51 \ jre \ lib \ rt.jar에 대한 제한으로 인해 PathDasher 유형에 액세스 할 수 없습니다.
realPK

7
이것은 InternalErrorJDK 1.8에서 발생합니다. JVM이 더 이상 실패하지 않습니다.
Sotirios Delimanolis

18

내가 마지막으로 시도했을 때 그것을 할 것입니다 :

public class Recur {
    public static void main(String[] argv) {
        try {
            recur();
        }
        catch (Error e) {
            System.out.println(e.toString());
        }
        System.out.println("Ended normally");
    }
    static void recur() {
        Object[] o = null;
        try {
            while(true) {
                Object[] newO = new Object[1];
                newO[0] = o;
                o = newO;
            }
        }
        finally {
            recur();
        }
    }
}

생성 된 로그 파일의 첫 부분 :

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x000000006dad5c3d, pid=6752, tid=1996
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode windows-amd64)
# Problematic frame:
# V  [jvm.dll+0x2e5c3d]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x00000000014c6000):  VMThread [stack: 0x0000000049810000,0x0000000049910000] [id=1996]

siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x0000000000000001 0x0000000049813fe8 

Registers:
EAX=0x000000006dc83090, EBX=0x000000003680f400, ECX=0x0000000005d40ce8, EDX=0x000000003680f400
ESP=0x0000000049813ff0, EBP=0x00000000013f2df0, ESI=0x00000000013f0e40, EDI=0x000000003680f400
EIP=0x000000006dad5c3d, EFLAGS=0x0000000000010206

Java 코드를 사용하여 순수하게 수행하는 방법을 보여주는 유일한 두 가지 답변 중 하나이며 완전한 코드가 포함되어 있음을 감안할 때 downvote에 약간 즐겁게 생각합니다.
핫 릭

다운 보트는 보증 할 수 없음에 동의합니다. 이는 실제 충돌이지만 인터뷰 질문에 어떻게 응답 하시겠습니까? 이전에 연구하고 암기하지 않았다면 인터뷰에서 답변으로 제공 할 수 없었으며 어쨌든 중립에서 열악한 답변을 고려할 수 있다면 문제에 어떻게 접근했는지 보여주지 않습니다. 나는 당신이 jvm 버그 익스플로잇을 위해 구글을 기대하고 훌륭한 답변을 구현했습니다.
Bill K

@ BillK-아니요, 위의 내용은 전적으로 내 작품이지만 몇 년 전에 여러 가지를 실험 해 보았습니다. 인터뷰에서 "try / catch 및 recursion을 사용하여 완료했지만 지금은 정확한 코드를 래틀 링 할 수 없습니다."라고 말할 수 있습니다.
핫 릭

1
세부 사항 (JDK 버전, VM 인수)은 무엇입니까? "11.2-b0"버전이 확실하지 않습니다. 나는 이것을 실행하고 있지만 많은 CPU를 소비합니다.
Stefan Reich

@Hick Licks : JVM 크래쉬 예제의 개념은 무엇입니까? JVM이 코드에 대해 충돌하는 이유 모든 스레드에는 별도의 스택 스레드가 있습니다.
VJS

15

완벽한 JVM 구현은 결코 중단되지 않습니다.

JNI 외에 JVM을 중단하려면 VM 자체에서 버그를 찾아야합니다. 무한 루프는 CPU를 소비합니다. 무한히 메모리를 할당하면 제대로 빌드 된 JVM에서 OutOfMemoryError가 발생해야합니다. 이로 인해 다른 스레드에 문제가 발생할 수 있지만 양호한 JVM은 여전히 ​​충돌하지 않아야합니다.

VM의 소스 코드에서 버그를 발견하고 예를 들어 VM 구현의 메모리 사용에 세그먼테이션 오류가 발생하면 실제로 충돌 할 수 있습니다.


14

JVM을 중단하려면 Sun JDK 1.6_23 이하에서 다음을 사용하십시오.

Double.parseDouble("2.2250738585072012e-308");

이것은 Sun JDK 의 버그 때문 입니다. OpenJDK에서도 발견되었습니다. 이것은 Oracle JDK 1.6_24부터 수정되었습니다.


10

충돌의 의미에 따라 다릅니다.

스택 공간이 부족해 지도록 무한 재귀를 수행 할 수 있지만 "정상적으로"중단됩니다. 예외가 발생하지만 JVM 자체가 모든 것을 처리합니다.

JNI를 사용하여 기본 코드를 호출 할 수도 있습니다. 제대로하지 않으면 충돌을 일으킬 수 있습니다. 이러한 충돌을 디버깅하는 것은 "재미"입니다 (서명 된 Java 애플릿에서 호출하는 큰 C ++ DLL을 작성해야했습니다). :)


6

Jon Meyer의 Java Virtual Machine 이라는 책 에는 JVM이 코어 덤프되도록하는 일련의 바이트 코드 명령에 대한 예가 있습니다. 이 책의 사본을 찾을 수 없습니다. 누군가가 있다면 그것을 찾아 답을 게시하십시오.


5

winxpsp2에서 wmp10으로 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

이로 인해 스폰 된 스레드가 catch되지 않은 Throwable을 발생시키고 핫스팟이 충돌합니다.

YMMV


5

손상된 하드웨어는 모든 프로그램과 충돌 할 수 있습니다. 한 번만 특정 컴퓨터에서 앱 충돌이 발생하여 정확히 동일한 설정으로 다른 컴퓨터에서 정상적으로 실행되었습니다. 머신에 RAM에 결함이 있음을 나타냅니다.


5

가장 짧은 방법 :)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}

충돌하지 않습니다. 컴파일 시간 오류가 발생 Exception in thread "main" java.lang.StackOverflowError at Test.main합니다. jdk1.8.0_65를 사용하고 있습니다
Quazi Irfan

5

충돌은 아니지만 허용 된 답변보다 충돌에 더 가깝습니다. System.exit

다음을 호출하여 JVM을 중지 할 수 있습니다.

Runtime.getRuntime().halt( status )

문서에 따르면 :-

"이 방법은 종료 후크가 시작되지 않고 종료시 종료가 활성화 된 경우 호출되지 않은 종료자를 실행하지 않습니다".



4

당신은 당신이 할 수있는 메모리가 부족한 척하려는 경우

public static void main(String[] args) {
    throw new OutOfmemoryError();
}

네이티브 메소드 (내장 메소드)를 호출하여 JVM이 오류 파일을 덤프하게하는 몇 가지 방법을 알고 있지만 아마도이 방법을 모르는 것이 가장 좋습니다. ;)


4

처리되지 않은 상황 (예 : Java 예외 또는 오류 없음)으로 인해 충돌을 프로세스 중단으로 정의한 경우 (sun.misc.Unsafe 클래스를 사용할 권한이없는 경우) Java 내에서이를 수행 할 수 없습니다. 이것이 관리 코드의 요점입니다.

네이티브 코드의 일반적인 충돌은 잘못된 메모리 영역 (널 주소 또는 정렬 오류)에 대한 포인터를 역 참조함으로써 발생합니다. 다른 소스는 잘못된 기계 명령어 (opcode)이거나 라이브러리 또는 커널 호출에서 처리되지 않은 신호일 수 있습니다. JVM 또는 시스템 라이브러리에 버그가있는 경우 둘 다 트리거 될 수 있습니다.

예를 들어 JITed (생성 된) 코드, 기본 메소드 또는 시스템 호출 (그래픽 드라이버)은 실제 충돌로 이어질 수 있습니다 (ZIP 함수를 사용하고 메모리가 부족할 때 충돌이 발생하는 것이 일반적 임). 이러한 경우 JVM의 크래시 핸들러가 시작되어 상태를 덤프합니다. 또한 OS 코어 파일 (Windows의 Dr. Watson 및 * nix의 코어 덤프)을 생성 할 수 있습니다.

Linux / Unix에서는 실행중인 프로세스에 신호를 보내 JVM 충돌을 쉽게 일으킬 수 있습니다. 참고 : SIGSEGV핫스팟은이 신호를 잡아서 대부분의 장소에서 NullPointerException으로 다시 던지므로이 기능을 사용해서는 안됩니다 . SIGBUS예를 들어 보내는 것이 좋습니다 .


3

JNI는 큰 충돌 원인입니다. JVMTI 인터페이스를 사용하여 C / C ++로 작성해야하므로 충돌 할 수도 있습니다.


2

더 많은 스레드를 생성하는 스레드 프로세스를 생성하면 (더 많은 스레드를 생성하는 등) 결국 JVM 자체에서 스택 오버플로 오류가 발생합니다.

public class Crash {
    public static void main(String[] args) {

        Runnable[] arr = new Runnable[1];
        arr[0] = () -> {

            while (true) {
                new Thread(arr[0]).start();
            }
        };

        arr[0].run();
    }
}

이것은 나에게 출력을 주었다 (5 분 후에 램을보십시오)

An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000070e53ed7, pid=12840, tid=0x0000000000101078
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# 

1

"충돌"이라는 의미 에서 JVM이 hs_err_pid % p.log에 쓰이게하는 등 갑자기 중단되는 JVM을 의미하는 경우이 방법으로 수행 할 수 있습니다.

-Xmx arg를 작은 ​​값으로 설정하고 메모리 부족으로 인해 충돌이 발생하도록 JVM에 지시하십시오.

 -Xmx10m -XX:+CrashOnOutOfMemoryError

분명히 위의 두 번째 인수가 없으면 jvm 이 OutOfMemoryError로 종료 되지만 jvm 이 "충돌"되거나 갑자기 중단되지는 않습니다.

이 기술은 JVM -XX : ErrorFile arg를 테스트 할 때 도움이되었으며, 이러한 hs_err_pid 로그를 작성해야하는 위치를 제어합니다. 그런 충돌을 일으키는 방법을 찾으려고 하면서이 게시물을 찾았습니다. 나중에 위의 내용이 내 필요에 가장 쉬운 것으로 밝혀지면 여기에 목록에 추가하고 싶었습니다.

마지막으로, FWIW, 누군가가 이미 args에 -Xms 값을 설정했을 때 이것을 테스트 할 수 있다면 (위보다 더 큰 값으로), 제거하거나 변경하고 싶거나 충돌하지 않고 단순히 "초기 힙 크기가 최대 힙 크기보다 큰 값으로 설정 됨"을보고하는 jvm 시작 실패 (일부 응용 프로그램 서버와 같은 서비스로 JVM을 실행하는 경우 분명하지 않습니다. 다시 한 번 말하지만 공유하고 싶었습니다.)


0

무한 for 루프를 동일한 함수에 대한 재귀 호출로 변경하면 스택 오버플로 예외가 발생합니다.

public static void main(String[] args) {
    causeStackOverflow();
}

public void causeStackOverflow() {
    causeStackOverflow();
}

0

나는 지금하고 있지만, 어떻게 ... :-) JVM (및 내 응용 프로그램)이 때로는 완전히 사라지는지를 완전히 확신하지는 못합니다. 오류가 발생하지 않았으며 아무것도 기록되지 않았습니다. 경고없이 즉시 일을 시작하지 않고 즉시 실행되지 않습니다.


하드웨어, 특히 메모리 검사를 시작하십시오!
Thorbjørn Ravn Andersen

불행히도, 그렇지 않으면 제대로 작동하는 여러 컴퓨터에 있습니다. 그것을 수행하는 것은이 하나의 특정 응용 프로그램뿐입니다 (메모리 또는 프로세서 집약적이 아님).
Brian Knoblauch

0

최단? 로봇 클래스를 사용하여 CTRL + BREAK를 트리거합니다. 콘솔을 닫지 않고 프로그램을 닫으려고 할 때 이것을 발견했습니다 ( '종료'기능이 없음).


오래된 질문-희망적으로 누군가가 당신의 대답에서 미래에 도움이되기를 바랍니다.
dbmitch

0

이 계산입니까?

long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}

Linux 및 Java 9에서만 작동합니다.

어떤 이유로 나는 얻지 ProcessHandle.current().destroyForcibly();못하고 JVM을 죽이지 않고 현재 프로세스java.lang.IllegalStateException 의 메시지 destroy not allowed 메시지를 던집니다 .


0

JVM 충돌을 복제하려고 할 때이 문제가 발생합니다.

Jni는 작동하지만 다른 플랫폼에 맞게 조정해야합니다. 결국이 조합을 사용하여 JVM 충돌을 일으 킵니다.

  1. 이 JVM 옵션으로 애플리케이션을 시작하십시오. -XX:+CrashOnOutOfMemoryError
  2. a long[] l = new long[Integer.MAX_VALUE];를 사용 하여 OOM을 트리거하십시오.

그러면 JVM이 충돌하고 충돌 로그가 생성됩니다.


-2

'충돌'이 jvm / 프로그램을 정상적인 종료에서 방해하는 것이면 처리되지 않은 예외가이를 수행 할 수 있습니다.

public static void main(String args[]){
   int i = 1/0;
   System.out.print(i); // This part will not be executed due to above  unhandled exception
  }

따라서 어떤 유형의 CRASH에 달려 있습니까?!


3
예외를 던지는 것은 충돌이 아닙니다.
Quazi Irfan 2016 년

처리되지 않은 런타임 예외는하지만, 충돌입니다 ArithmeticException입니다
중간
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.