javac의 정적 최종 변수 인라인을 비활성화할 수 있습니까?
Java 정적 컴파일러(javac)는 일부 정적 최종 변수를 인라인하고 값을 상수 풀로 직접 가져옵니다. 다음 예를 고려하십시오. 클래스 A는 몇 가지 상수(공개 정적 최종 변수)를 정의합니다.
public class A {
public static final int INT_VALUE = 1000;
public static final String STRING_VALUE = "foo";
}
클래스 B는 다음 상수를 사용합니다.
public class B {
public static void main(String[] args) {
int i = A.INT_VALUE;
System.out.println(i);
String s = A.STRING_VALUE;
System.out.println(s);
}
}
클래스 B를 컴파일할 때 javac는 클래스 A에서 이러한 상수 값을 가져오고 이러한 값을 B.class에 인라인합니다. 결과적으로 컴파일 시간에 클래스 A에 대한 종속성 B가 바이트 코드에서 지워집니다. 이것은 컴파일 시 이러한 상수 값을 베이킹하기 때문에 다소 특이한 동작 입니다 . 그리고 이것이 JIT 컴파일러가 런타임에 할 수 있는 가장 쉬운 일 중 하나라고 생각할 것입니다.
javac의 인라인 동작을 비활성화할 수 있는 방법이나 숨겨진 컴파일러 옵션이 있습니까? 배경으로, 우리는 종속성 목적으로 바이트코드 분석을 수행하는 방법을 찾고 있으며, 이는 바이트코드 분석이 컴파일 타임 종속성을 감지하지 못하는 몇 안 되는 경우 중 하나입니다. 감사 해요!
편집 : 일반적으로 모든 소스(예: 상수를 정의하는 타사 라이브러리)를 제어하지 않기 때문에 이것은 성가신 문제입니다. 우리는 상수 사용 의 관점에서 이러한 종속성을 감지하는 데 관심이 있습니다. 상수 를 사용 하는 코드에서 참조가 지워지기 때문에 소스 코드 분석을 하지 않는 한 쉽게 감지할 수 있는 방법이 없습니다.
Java Puzzlers(Joshua Bloch)의 항목 93에서는 최종 값이 상수로 간주되지 않도록 하여 이 문제를 해결할 수 있다고 말합니다. 예를 들어:
public class A {
public static final int INT_VALUE = Integer.valueOf(1000).intValue();
public static final String STRING_VALUE = "foo".toString();
}
물론 상수를 정의하는 코드에 액세스할 수 없다면 이 중 아무 것도 관련이 없습니다.
나는 그렇게 믿지 않는다. 가장 간단한 해결 방법은 다음을 필드가 아닌 속성으로 노출하는 것입니다.
public class A {
private static final int INT_VALUE = 1000;
private static final String STRING_VALUE = "foo";
public static int getIntValue() {
return INT_VALUE;
}
public static String getStringValue() {
return STRING_VALUE;
}
}
어떤 경우에는 인라인이 값의 사용에 필수적이라는 것을 잊지 마십시오 - 예를 들어, 사용 인 경우에 INT_VALUE
스위치 블록의 경우,로 가 일정한 값으로 지정해야합니다.
인라인을 중지하려면 값을 컴파일하지 않는 시간 상수(JLS 용어)로 만들어야 합니다. 함수를 사용하지 않고 null
이니셜라이저 표현식에서 a 를 사용하여 최소한의 바이트 코드를 생성하지 않고도 이 작업을 수행할 수 있습니다 .
public static final int INT_VALUE = null!=null?0: 1000;
코드 생성에 있어 매우 문자적이지만 javac
정적 이니셜라이저의 정적 필드에 저장이 뒤따르는 정수를 즉시 푸시하도록 최적화해야 합니다.
JLS 13.4.9 는 이 문제를 다룹니다. 값이 변경될 가능성이 있는 경우 기본적으로 컴파일 시간 상수를 피하는 것이 좋습니다.
(상수를 인라인해야 하는 한 가지 이유는 switch 문에는 각 경우에 상수가 필요하고 이러한 상수 값이 두 개일 수 없기 때문입니다. 컴파일러는 컴파일 시간에 switch 문에서 중복 상수 값을 확인합니다. 클래스 파일 형식은 그렇지 않습니다. 케이스 값의 심볼릭 링크를 수행하십시오.)
널리 배포된 코드에서 "일정하지 않은 상수" 문제를 피하는 가장 좋은 방법은 실제로 변경되지 않을 것 같은 값만 컴파일 시간 상수로 선언하는 것입니다. 진정한 수학 상수의 경우를 제외하고 소스 코드에서 static 및 final로 선언된 클래스 변수를 매우 드물게 사용하는 것이 좋습니다. final의 읽기 전용 특성이 필요한 경우 더 나은 선택은 개인용 정적 변수와 값을 가져오기 위한 적절한 접근자 메서드를 선언하는 것입니다. 따라서 다음을 권장합니다.
private static int N; public static int getN() { return N; }
보다는:
public static final int N = ...;
다음에는 문제가 없습니다.
public static int N = ...;
N이 읽기 전용일 필요가 없는 경우.
이것은 심각한 버그 라고 생각합니다 . 자바는 C/C++가 아닙니다. "한 번 컴파일하고 모든 곳에서 실행"이라는 원칙이 있습니다.
이 경우 Class A가 변경됩니다. A.CONST_VALUE를 참조하는 모든 클래스는 다시 컴파일해야 하며 클래스 A가 변경되었는지 여부를 거의 알지 못합니다.
클래스 A를 다음과 같이 다시 작성하십시오.
public class A {
public static final int INT_VALUE;
public static final String STRING_VALUE;
static {
INT_VALUE = 1000;
STRING_VALUE = "foo";
}
}
jmake 는 Java 파일 간의 종속성을 추적하고 필요한 최소 파일 세트를 점진적으로 컴파일하는 전체 작업을 수행한다고 주장하는 오픈 소스 프로젝트입니다. 때로는 전체 프로젝트를 다시 컴파일해야 하지만 정적 최종 상수에 대한 변경 사항을 올바르게 처리한다고 주장합니다. 클래스 파일보다 더 세밀하게 변경 사항을 처리합니다. 예를 들어 Cm() 메서드의 서명이 변경되면 C를 사용하는 모든 클래스가 아니라 실제로 m()에 의존하는 클래스만 다시 컴파일합니다.
면책 조항: 저는 jmake를 사용한 경험이 없습니다.
나는 최근에 비슷한 문제 를 발견했으며 위에서 말했듯이 컴파일 시간이 아닌 표현식을 사용하여 이러한 인라인을 해결할 수 있습니다.
public final class A {
public static final int INT_VALUE = constOf(1000);
public static final String STRING_VALUE = constOf("foo");
}
여기서 constOf
메소드 패밀리는 다음과 같습니다.
// @formatter:off
public static boolean constOf(final boolean value) { return value; }
public static byte constOf(final byte value) { return value; }
public static short constOf(final short value) { return value; }
public static int constOf(final int value) { return value; }
public static long constOf(final long value) { return value; }
public static float constOf(final float value) { return value; }
public static double constOf(final double value) { return value; }
public static char constOf(final char value) { return value; }
public static <T> T constOf(final T value) { return value; }
// @formatter:on
이것은 Integer.valueOf(1000).intValue()
또는 다음 과 같은 다른 제안보다 약간 짧습니다.null!=null?0: 1000
Java가 동적 컴파일에 밀접하게 의존하고 있으며 C++와 같은 멋진 컴파일 논리를 수행하지 않는다고 생각합니다.
이것을 비활성화/활성화하는 몇 가지 옵션이 있을 수 있는 런타임 최적화를 수행하는 JIT 컴파일러로 몇 가지 옵션을 시험해 볼 수 있습니다.
기본 javac에서는 해당 옵션을 얻지 못할 수 있습니다. 1. 확장 또는 구현과 같은 일부 유형의 종속성 그래프를 사용해야 합니다. 2. 메서드 기반 연결을 사용합니다.
-NS
ReferenceURL : https://stackoverflow.com/questions/3524150/is-it-possible-to-disable-javacs-inlining-of-static-final-variables
'IT이야기' 카테고리의 다른 글
8진수를 사용할 때 잘못된 토큰 (0) | 2021.10.18 |
---|---|
Android의 파일 시스템 (0) | 2021.10.18 |
ERb 시퀀스 끝에 '-%>'(빼기 기호) (0) | 2021.10.17 |
일반 용어로 눈에 거슬리지 않는 Javascript (0) | 2021.10.17 |
함수의 arity 얻기 (0) | 2021.10.17 |