As given with the simple attached code, a GridPane starts to act strangly, given mixed ColumnContraints:
With a Scene/GridPane of 500 px in width and the contraints set to {20%, hgrow.MAX, 20%, hgrow.MAX, 20%} I expect the GridPane to have the columns (100px, 100px, 100px, 100px, 100px).
This is true for an empty GridPane, as it can be seen at first in the attached screenshot.
If I add a Label to the GridPane which "concerns" the hgrowbased column  which means it's added directy to it or its spanattribute "touches" it  columns begin to resize inapropiately, even if the Label should fit. ScenicView tells me that the Label's pref/maxSize is 86px, so it should fit into a 100px wide column...
Important: If I set the first hgrowcolumn to 20% everything is fine and no strange resizing happens...
UPDATE:
I've tracked it down with the GridPane's sourceCode, while working with a GridPane having every column filled with a Label. Before the method "growOrShrinkColumnWidths" gets called, widths are:
[100.0, 86.0, 100.0, 44.0, 100.0]
which means we have 500  100  86 100  44  100 = 70 px available.
Now, within growOrShrinkColumnWidths, we iterate through each adjustment:
while (Math.abs(available) > 1.0 && adjusting.size() > 0) {
final double portion = available / adjusting.size(); // negative in shrinking case // ON FIRST ITERATION ITS 35px
for (int i = 0; i < adjusting.size(); i++) {
final int index = adjusting.get(i);
final double limit = (shrinking? columnMinWidth[index] : columnMaxWidth[index])
 columnWidths[index]; // negative in shrinking case // LIMIT IS HUGE, since MAXWIDTHS IS [MAX, MAX, MAX, MAX, MAX]
final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
columnWidths[index] += change; // HERES THE MISTAKE: WE ADD 35px instead of 14px, which would be needed to establish a even distribution of remaining space...
//if (node.id.startsWith("debug.")) println("{if (shrinking) "vshrink" else "vgrow"}: {node.id} portion({portion})=available({available})/({sizeof adjusting}) change={change}");
available = change;
if (Math.abs(change) < Math.abs(portion)) {
adjusted.add(index);
}
}
for (int i = 0; i < adjusted.size(); i++) {
adjusting.remove(adjusted.get(i));
}
adjusted.clear();
}
With a Scene/GridPane of 500 px in width and the contraints set to {20%, hgrow.MAX, 20%, hgrow.MAX, 20%} I expect the GridPane to have the columns (100px, 100px, 100px, 100px, 100px).
This is true for an empty GridPane, as it can be seen at first in the attached screenshot.
If I add a Label to the GridPane which "concerns" the hgrowbased column  which means it's added directy to it or its spanattribute "touches" it  columns begin to resize inapropiately, even if the Label should fit. ScenicView tells me that the Label's pref/maxSize is 86px, so it should fit into a 100px wide column...
Important: If I set the first hgrowcolumn to 20% everything is fine and no strange resizing happens...
UPDATE:
I've tracked it down with the GridPane's sourceCode, while working with a GridPane having every column filled with a Label. Before the method "growOrShrinkColumnWidths" gets called, widths are:
[100.0, 86.0, 100.0, 44.0, 100.0]
which means we have 500  100  86 100  44  100 = 70 px available.
Now, within growOrShrinkColumnWidths, we iterate through each adjustment:
while (Math.abs(available) > 1.0 && adjusting.size() > 0) {
final double portion = available / adjusting.size(); // negative in shrinking case // ON FIRST ITERATION ITS 35px
for (int i = 0; i < adjusting.size(); i++) {
final int index = adjusting.get(i);
final double limit = (shrinking? columnMinWidth[index] : columnMaxWidth[index])
 columnWidths[index]; // negative in shrinking case // LIMIT IS HUGE, since MAXWIDTHS IS [MAX, MAX, MAX, MAX, MAX]
final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
columnWidths[index] += change; // HERES THE MISTAKE: WE ADD 35px instead of 14px, which would be needed to establish a even distribution of remaining space...
//if (node.id.startsWith("debug.")) println("{if (shrinking) "vshrink" else "vgrow"}: {node.id} portion({portion})=available({available})/({sizeof adjusting}) change={change}");
available = change;
if (Math.abs(change) < Math.abs(portion)) {
adjusted.add(index);
}
}
for (int i = 0; i < adjusted.size(); i++) {
adjusting.remove(adjusted.get(i));
}
adjusted.clear();
}
 relates to

JDK8123704 Regression: GridPane ColumnConstraints/RowConstraints alignment property is ignored
 Resolved